安全系列文章首发javascript
下文将以 Java 为例,对上述三种方法分别用代码进行示例。不管使用何种方法,在服务器端的拦截器必不可少,它将负责检查到来的请求是否符合要求,而后视结果而决定是否继续请求或者丢弃。在 Java 中,拦截器是由 Filter 来实现的。咱们能够编写一个 Filter,并在 web.xml 中对其进行配置,使其对于访问全部须要 CSRF 保护的资源的请求进行拦截。html
在 filter 中对请求的 Referer 验证代码以下java
// 从 HTTP 头中取得 Referer 值
String referer=request.getHeader("Referer");
// 判断 Referer 是否以 bank.example 开头
if((referer!=null) &&(referer.trim().startsWith(“bank.example”))){
chain.doFilter(request, response);
}else{
request.getRequestDispatcher(“error.jsp”).forward(request,response);
}
复制代码
以上代码先取得 Referer 值,而后进行判断,当其非空并以 bank.example 开头时,则继续请求,不然的话多是 CSRF 攻击,转到 error.jsp 页面。web
若是要进一步验证请求中的 token 值,代码以下算法
HttpServletRequest req = (HttpServletRequest)request;
HttpSession s = req.getSession();
// 从 session 中获得 csrftoken 属性
String sToken = (String)s.getAttribute(“csrftoken”);
if(sToken == null){
// 产生新的 token 放入 session 中
sToken = generateToken();
s.setAttribute(“csrftoken”,sToken);
chain.doFilter(request, response);
} else{
// 从 HTTP 头中取得 csrftoken
String xhrToken = req.getHeader(“csrftoken”);
// 从请求参数中取得 csrftoken
String pToken = req.getParameter(“csrftoken”);
if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){
chain.doFilter(request, response);
}else if(sToken != null && pToken != null && sToken.equals(pToken)){
chain.doFilter(request, response);
}else{
request.getRequestDispatcher(“error.jsp”).forward(request,response);
}
}
复制代码
首先判断 session 中有没有 csrftoken,若是没有,则认为是第一次访问,session 是新创建的,这时生成一个新的 token,放于 session 之中,并继续执行请求。若是 session 中已经有 csrftoken,则说明用户已经与服务器之间创建了一个活跃的 session,这时要看这个请求中有没有同时附带这个 token,因为请求可能来自于常规的访问或是 XMLHttpRequest 异步访问,咱们分别尝试从请求中获取 csrftoken 参数以及从 HTTP 头中获取 csrftoken 自定义属性并与 session 中的值进行比较,只要有一个地方带有有效 token,就断定请求合法,能够继续执行,不然就转到错误页面。生成 token 有不少种方法,任何的随机算法均可以使用,Java 的 UUID 类也是一个不错的选择。安全
除了在服务器端利用 filter 来验证 token 的值之外,咱们还须要在客户端给每一个请求附加上这个 token,这是利用 js 来给 html 中的连接和表单请求地址附加 csrftoken 代码,其中已定义 token 为全局变量,其值能够从 session 中获得。服务器
function appendToken(){
updateForms();
updateTags();
}
function updateForms() {
// 获得页面中全部的 form 元素
var forms = document.getElementsByTagName('form');
for(i=0; i<forms.length; i++) {
var url = forms[i].action;
// 若是这个 form 的 action 值为空,则不附加 csrftoken
if(url == null || url == "" ) continue;
// 动态生成 input 元素,加入到 form 以后
var e = document.createElement("input");
e.name = "csrftoken";
e.value = token;
e.type="hidden";
forms[i].appendChild(e);
}
}
function updateTags() {
var all = document.getElementsByTagName('a');
var len = all.length;
// 遍历全部 a 元素
for(var i=0; i<len; i++) {
var e = all[i];
updateTag(e, 'href', token);
}
}
function updateTag(element, attr, token) {
var location = element.getAttribute(attr);
if(location != null && location != '' '' ) {
var fragmentIndex = location.indexOf('#');
var fragment = null;
if(fragmentIndex != -1){
//url 中含有只至关页的锚标记
fragment = location.substring(fragmentIndex);
location = location.substring(0,fragmentIndex);
}
var index = location.indexOf('?');
if(index != -1) {
//url 中已含有其余参数
location = location + '&csrftoken=' + token;
} else {
//url 中没有其余参数
location = location + '?csrftoken=' + token;
}
if(fragment != null){
location += fragment;
}
element.setAttribute(attr, location);
}
}
复制代码
在客户端 html 中,主要是有两个地方须要加上 token,一个是表单 form,另外一个就是连接 a。这段代码首先遍历全部的 form,在 form 最后添加一隐藏字段,把 csrftoken 放入其中。而后,代码遍历全部的连接标记 a,在其 href 属性中加入 csrftoken 参数。注意对于 a.href 来讲,可能该属性已经有参数,或者有锚标记。所以须要分状况讨论,以不一样的格式把 csrftoken 加入其中。session
若是你的网站使用 XMLHttpRequest,那么还须要在 HTTP 头中自定义 csrftoken 属性,利用 dojo.xhr 给 XMLHttpRequest 加上自定义属性代码以下:app
var plainXhr = dojo.xhr;
// 重写 dojo.xhr 方法
dojo.xhr = function(method,args,hasBody) {
// 确保 header 对象存在
args.headers = args.header || {};
tokenValue = '<%=request.getSession(false).getAttribute("csrftoken")%>';
var token = dojo.getObject("tokenValue");
// 把 csrftoken 属性放到头中
args.headers["csrftoken"] = (token) ? token : " ";
return plainXhr(method,args,hasBody);
};
复制代码
这里改写了 dojo.xhr 的方法,首先确保 dojo.xhr 中存在 HTTP 头,而后在 args.headers 中添加 csrftoken 字段,并把 token 值从 session 里拿出放入字段中。异步