csrf 防御

http 中 origin 的含义:
跨域资源共享策略(cors)中,当浏览器发出跨域请求,会自动为请求头带上 origin 头部

防护 csrf 的方法:
1.通过请求头判断请求是否与当前页面同源
检验 referer 字段,如果不存在则拒绝该请求!
注意,在检验的时候容易被绕过。如果你的站点是 site.com,要确保referer中是 site.com.attacker.com 不会被允许!

 // 从 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); 
 }

2.csrf token
当用户请求一个网页时候,web 应用生成一个与当前 session 相关的 token,这个值存在服务器的 session 中,并且把 token 发送给客户端。当客户端发送"修改密码"的请求的时候,在表单中用一个隐藏字段发送该 token ,服务器验证验证 session 中存储的 token 与客户端发送来的 token 是否相同,如果不相同或者没有 token ,则抛弃该请求。

在 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); 
    } 
 }

在客户端对于请求加入 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); 
    } 
 }

推荐阅读更多精彩内容