CSRF(Cross Site Request Forgery, 跨站请求伪造)

摘要:
但是这个请求来自Mallory而非Bob,他不能通过安全认证,因此该请求不会起作用。account=bob&amount=1000000&for=Mallory”,并且通过广告等诱使Bob来访问他的网站。这时,悲剧发生了,这个url请求就会得到响应,钱将从Bob的账号转移到Mallory的账号,而Bob当时毫不知情。

一、CSRF 背景与介绍

CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一。其他安全隐患,比如 SQL 脚本注入,跨站域脚本攻击等在近年来已经逐渐为众人熟知,很多网站也都针对他们进行了防御。然而,对于大多数人来说,CSRF 却依然是一个陌生的概念。即便是大名鼎鼎的 Gmail, 在 2007 年底也存在着 CSRF 漏洞,从而被黑客攻击而使 Gmail 的用户造成巨大的损失。

二、CSRF 攻击实例

CSRF(Cross Site Request Forgery, 跨站请求伪造)第1张

CSRF 攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在并未授权的情况下执行在权限保护之下的操作。比如说,受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。

三、CSRF 攻击的对象

在讨论如何抵御 CSRF 之前,先要明确 CSRF 攻击的对象,也就是要保护的对象。从以上的例子可知,CSRF 攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF 的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。

四、防护CSRF 攻击

1、Django 提供的 CSRF 防护机制

1、使用原理

django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,这样就能避免被 CSRF 攻击

2、原理过程  

1、GET 请求不要用有副作用。也就是说任何处理 GET 请求的代码对资源的访问都一定要是“只读“的,所以get方式发送数据不需要设置csrf。

2、在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token

3、在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段

4、在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.

2、Django 里如何使用 CSRF 防护(置防跨站请求伪造功能)

1、全局设置

启用 django.middleware.csrf.CsrfViewMiddleware 这个中间件

2、局部设置

from django.views.decorators.csrf import csrf_exempt,csrf_protect

@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。

@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

3、客户端如何正确发送数据才不会被csrf拦截

1、通过form提交

1、模板渲染

在html页面的form表单中添加{%csrf_token%}模板语言,,前提是在后端一定要使用render()的方法返回html数据,因为使用 render()会处理 csrf_token 这个模板语言, 从而自动为表单添加一个名   为 csrfmiddlewaretoken 的 input。

<form method="POST" action="/post-url/">
    {% csrf_token %}

    <div>......</div>
</form>

2、模板渲染原理

默认情况下form表单必须添加红色箭头所指的name和value值才能通过csrf验证,但是只有我们通过render()方法添加上模板语言就可以自动添加

CSRF(Cross Site Request Forgery, 跨站请求伪造)第2张

2、通过ajax提交

1、因为cookie中同样存在csrftoken,所以可以在js中通过:$.cooke("cstftoken")获取

在views中的login函数中:from django.conf import settings,然后打印print(settings.CSRF_HEADER_NAME)

print(settings.CSRF_HEADER_NAME)打印的内容为:HTTP_X_CSRFTOKEN

这里的HTTP_X_CSRFTOKEN是django在X_CSRF的前面添加了HTTP_,所以实际传递的是就是X_CSRFtoken,而在前端页面的ajax传递的时候由于不能使用下划线所以传递的是X_CSRFtoken

    $("#btn1").click(function () {
        $.ajax({
            url:"/login/",
            type:"POST",
            data:{"usr":"root","pwd":"123"},
            headers:{ "X-CSRFtoken":$.cookie("csrftoken")},
            success:function (arg) {

            }
        })
    })

2、如果页面中有多个ajax请求的话就需要在每个ajax中添加headers信息,所以可以通过下面方式在发送ajax请求之前就会自动执行$.ajaxSetup函数,该函数的作用范围是全局ajax;

也可以把beforeSend:function (xhr,settings) { xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken")) }这句话放在ajax的请求头中,该函数的作用范围是局部ajax.

   $.ajaxSetup({
            beforeSend:function (xhr,settings) {
                xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken"))
            }
        });

3、当发送数据方式为get方式的时候不需要提交csrftoken,为post方式的时候需要,所以该代码可改写成一下方式

function csrfSafeMethod(method) {
            // these HTTP methods do notrequire CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) &&!this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken",$.cookie("csrftoken"));
                }
            }
        });
注释:  这样就实现了当GET|HEAD|OPTIONS|TRACE这些方式请求的时候不需要提交csrftoken
View Code

4、通过jQuery插件(通过js代码导入jquery.cookie.js插件,该插件代码如下

/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2013Klaus Hartl
 *Released under the MIT license
 */(function (factory) {
    if (typeof define === 'function' &&define.amd) {
        //AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        //CommonJS
        factory(require('jquery'));
    } else{
        //Browser globals
        factory(jQuery);
    }
}(function ($) {

    var pluses = /+/g;

    function encode(s) {
        returnconfig.raw ? s : encodeURIComponent(s);
    }

    function decode(s) {
        returnconfig.raw ? s : decodeURIComponent(s);
    }

    function stringifyCookieValue(value) {
        returnencode(config.json ? JSON.stringify(value) : String(value));
    }

    function parseCookieValue(s) {
        if (s.indexOf('"') ===0) {
            // This isa quoted cookie as according to RFC2068, unescape...
            s = s.slice(1, -1).replace(/\"/g, '"').replace(/\\/g, '\');
}

        try{
            // Replace server-side written pluses with spaces.
            // If we can't decode the cookie, ignore it, it's unusable.
            // If we can't parse the cookie, ignore it, it's unusable.
            s = decodeURIComponent(s.replace(pluses, ' '));
            returnconfig.json ? JSON.parse(s) : s;
        } catch(e) {}
    }

    function read(s, converter) {
        var value =config.raw ? s : parseCookieValue(s);
        return$.isFunction(converter) ? converter(value) : value;
    }

    var config = $.cookie =function (key, value, options) {

        //Write

        if (value !== undefined &&!$.isFunction(value)) {
            options =$.extend({}, config.defaults, options);

            if (typeof options.expires === 'number') {
                var days = options.expires, t = options.expires =new Date();
                t.setTime(+t + days * 864e+5);
            }

            return (document.cookie =[
                encode(key), '=', stringifyCookieValue(value),
                options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is notsupported by IE
                options.path    ? '; path=' + options.path : '',
                options.domain  ? '; domain=' + options.domain : '',
                options.secure  ? '; secure' : ''].join(''));
        }

        //Read

        var result =key ? undefined : {};

        // To prevent the for loop inthe first place assign an empty array
        // incase there are no cookies at all. Also prevents odd result when
        //calling $.cookie().
        var cookies = document.cookie ? document.cookie.split('; ') : [];

        for (var i = 0, l = cookies.length; i < l; i++) {
            var parts = cookies[i].split('=');
            var name =decode(parts.shift());
            var cookie = parts.join('=');

            if (key && key ===name) {
                // If second argument (value) is a function it's a converter...
                result =read(cookie, value);
                break;
            }

            // Prevent storing a cookie that we couldn't decode.
            if (!key && (cookie = read(cookie)) !==undefined) {
                result[name] =cookie;
            }
        }

        returnresult;
    };

    config.defaults ={};

    $.removeCookie =function (key, options) {
        if ($.cookie(key) ===undefined) {
            returnfalse;
        }

        // Must notalter options, thus extending a fresh object...
        $.cookie(key, '', $.extend({}, options, { expires: -1}));
        return!$.cookie(key);
    };

}));
注释:html前端通过<script src="/static/plugins/bootstrap/js/bootstrap.js"></script>方式引入该插件
View Code

总结:

1、csrf在前端的key为:X-CSRFtoken,到后端的时候django会自动添加HTTP_,并且最后为HTTP_X_CSRFtoken

免责声明:文章转载自《CSRF(Cross Site Request Forgery, 跨站请求伪造)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux基础命令:netstatvisual studio code 输出乱码下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

sqlmap详细使用 [精简]

1. 基础用法: 一下./sqlmap.py 在kali和backtrack中使用sqlmap的时候,直接用:sqlmap ./sqlmap.py -u “注入地址” -v 1 –dbs // 列举数据库 ./sqlmap.py -u “注入地址” -v 1 –current-db // 当前数据库 ./sqlmap.py -u “注入地址” -v 1 –...

js设置滚动条位置

JS控制滚动条的位置: window.scrollTo(x,y); 竖向滚动条置顶(window.scrollTo(0,0); 竖向滚动条置底 window.scrollTo(0,document.body.scrollHeight); JS控制TextArea滚动条自动滚动到最下部 document.getElementById('textare...

csrf攻击原理和防御-生成token防御代码

前言 CSRF(Cross-site request forgery,跨站请求伪造,恶意网站伪造身份冒充你向目标服务器发送请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。 攻击原理 csrf攻击通过利用浏览器机制:发送请求的域相同时,浏览器会自动携带相...

分布式下Session一致性问题

一、Session一致性问题 1.1 什么是Session 用户使用网站的服务,基本上需要浏览器和web服务器进行多次交互,web服务器如何知道哪些请求是来自哪个会话的? 具体方式为:在会话开始时,分配一个唯一的会话标识(sessionId),通过cookie把这个标识告诉浏览器,以后每次请求的时候,浏览器都会带上这个会话标识来告诉web服务器请求是属于哪...

学会使用ip池和cookie池伪装

在进入正题之前,我们先复习一个关于requests模块的相关知识点: requests中解决编码的三种方法: ①response.content 类型:bytes 解码类型:没有指定 如何修改编码方式:response.content.decode() ②response.content.decode() 类型:str 解码类型:解码成python文本的字...

jQuery 操作Cookie

转自:http://www.cnblogs.com/luluping/archive/2009/04/23/1442465.html jQuery.cookie = function(name, value, options) {          if (typeof value != 'undefined') {                 ...