美文网首页
CSRF 跨站请求伪造

CSRF 跨站请求伪造

作者: 代码表演艺术家 | 来源:发表于2020-05-07 16:21 被阅读0次

    跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 **CSRF **或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。下面通过一个实例来说明:

    假如在某个网站上,用户可以修改自己的邮箱地址,这个修改的操作是通过POST请求到服务器上的一个url,就像这样:

    POST /email/change HTTP/1.1
    Host: website.com
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 30
    Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE
    
    email=jack@normal-user.com
    

    黑客研究了这个请求的规律后,自己搭建了这样一个页面:

    <html>
      <body>
        <form action="https://website.com/email/change" method="POST">
          <input type="hidden" name="email" value="hijack@evil-user.net" />
        </form>
        <script>
          document.forms[0].submit();
        </script>
      </body>
    </html>
    

    如果一个受害人进入了这个黑客页面,情况是:

    • 1.这个页面在受害人完全不知情的情况下就往website.com 发送了修改邮箱的请求
    • 2.如果受害人之前登录过website.com 网站,那么浏览器会自动带上website.com 网站的cookie(如果没有设置同源cookies的话)去请求website.com
    • 3.服务器只认cookie, 看到黑客的请求里带有cookie, 就当作正常的请求处理了,网站就遭受到了黑客攻击。

    django 的 CSRF防御

    django项目默认会在setting 里的MIDDLEWARE 配置里添加csrf的防御中间件 'django.middleware.csrf.CsrfViewMiddleware',原理是这样的:

    django在cookie里放置一个键csrftoken,这是一个64位的字符串,每次向服务器提交数据的时候都会带上这个cookie值,同时,在post或put, delete 数据时,数据里要有csrfmiddlewaretoken 这个特殊的数据,在后台django会比较cookie里的csrftoken和请求数据里的csrfmiddlewaretoken是否匹配,如果匹配才会正常处理,否则直接报错。

    django可以在form表单通过添加{% csrf_token %}来自动生成如下不可见的csrfmiddlewaretoken字段

    <input type="hidden" name="csrfmiddlewaretoken" value="kdA7zoTmrLpZkRU30IsLxNxzJzCxrw9y2pY2OCAzeKteRRi9M9woQknJq2ImxTLN">
    

    这个字段提交到后台时会通过CsrfViewMiddleware中间件来检查,不需要我们操作。

    注意:上面提到的检查csrftoken和csrfmiddlewaretoken匹配的操作不是简单的逐字比较是否相同,因为如果你刷新你的表单,会发现表单的csrfmiddlewaretoken在变,而cookie里的csrftoken并没有变。
    其实无论是csrftoken还是csrfmiddlewaretoken都是有两部分组成,一个随机的 salt 和 秘文secret,通过对csrftoken和csrfmiddlewaretoken分别进行加盐揭秘,最后比较是否相等。

    django ajax请求问题

    因为自己在javascript 里构造的ajax请求没有csrfmiddlewaretoken 这个值,所以一般ajax 通过post,put,delele 请求到后台会报csrf缺少错误,怎么办?
    网上很多的答案都是简单除暴的去掉csrf校验,比如通过删掉配置文件里的CsrfViewMiddleware中间件来全局禁用csrf,或者使用@csrf_exempt装饰器装饰view来局部禁用csrf校验,
    但这些方法都是不安全的,就好像你每次回家都要掏钥匙开锁觉得太麻烦,然后干脆出门不锁门一样,没有真的解决问题,而是逃避问题。

    正确的解决方法是在ajax请求里手动加上csrf,
    比如在模板里写ajax, 这种比较简单,但是不灵活, 没法写到js 文件里,并且每一个ajax请求都是加上csrfmiddlewaretoken

    $.ajax({
        data: {
            somedata: 'somedata',
            moredata: 'moredata',
            csrfmiddlewaretoken: '{{ csrf_token }}'
        },
    

    另一种改进的方法是设置全局的ajax参数,后面的所有请求都不用再添加csrfmiddlewaretoken了

    $.ajaxSetup({
      data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
    });
    

    上面两种代码因为使用了django变量,所以还是必须要写在模板文件里,不完美。

    完全脱离后端的方法是在js里获取cookie值,如下:

    function getCookie(name) {
    # 可以使用现成的js 库https://github.com/js-cookie/js-cookie/ 来代替getCookie这个函数
        var cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = cookies[i].trim();
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    var csrftoken = getCookie('csrftoken');
    
    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
    
    

    通过getCookie('csrftoken')获取cookie里的csrftoken,然后在$.ajaxSetup 设置请求头的X-CSRFToken 值(前面是设置请求体的csrfmiddlewaretoken),
    注意这里 function csrfSafeMethod 函数,不会对GET,HEAD,这些请求方式进行csrf保护。这就要求我们平时写代码时一定要遵守HTTP请求规范,不要在GET,HEAD,OPTIONS 这些请求里进行任何修改操作。有些人因为GET请求比较简单,把增删改查都用GET请求操作,虽然功能上可以实现,但是很危险,
    举个经典的例子,假如你的网站有个链接是这样的:

    href = "myAppDeleteImportantData.aspx?UserID=27"
    

    并且google-bot出现并为您的页面编制索引?那么会发生什么?

    相关文章

      网友评论

          本文标题:CSRF 跨站请求伪造

          本文链接:https://www.haomeiwen.com/subject/hqfzwhtx.html