CSRF跨站请求伪造漏洞修复方案

CSRF(全称Cross-site request forgery)即跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或包含攻击代码的页面,在受害者不知情的情况下以受害者的身份(身份认证所对应的信息)向服务器发送请求,从而完成非法操作(如转账、改密等)。由于发生CSRF攻击后,攻击者是强迫用户向服务器发送请求,所以会造成用户信息被迫修改,更严重者引发蠕虫攻击。
CSRF攻击原理及过程:
1、用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2、在用户信息通过验证后,网站A产生cookie信息并返回给浏览器,此时用户登录网站A成功,以正常发送请求到网站A;
3、用户未退出网站A之前,在同一浏览器中,打开一个tab页访问网站B
4、网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A
5、浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带cookie信息,向网站A发出请求,导致来自网站B的恶意代码被执行。
CSRF防护建议
1、验证http referer字段
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限页面的请求必须来自于同一个网站。
2、在请求地址中添加token并验证
CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的cookie进行安全验证。所以,抵御CSRF攻击的关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于cookie中。因此,可以在http请求中以参数的形式加入一个随机产生的token,并在服务器建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求。
3、在http头中自定义属性并验证
自定义属性的方法也是使用token并进行验证,和前一种方法的不同的是,不是把token以参数的形式置于http请求中,而是把它放在http头中自定义的属性中。通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个http头属性,并把token放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过referer泄露网站地址。
4、其他防御方法
<1>CSRF攻击是有条件的,当用户访问恶意链接时,认证的cookie仍然有效,所以当用户关闭页面时要及时清除认证cookie,对支持tab模式(新标签打开网页)的浏览器尤为重要。
<2>尽量少用或不使用request()类变量,获取参数指定request.form()还是request.querystring(),这样有利于阻止CSRF漏洞攻击,此方法只不能完全防御CSRF攻击,只是一定程度上增加了攻击难度。
Java代码示例:
1、在filter中验证referer

// 从 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 页面。
2、在filter中验证请求中的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,就判定请求合法,可以继续执行,否则就转到错误页面。
3、在客户端对于请求附加token

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 加入其中。
4、在http头中自定义属性

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);
 
 }; 
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值