一、什么是CSRF

    CSRF, Cross Site Request Forgery, 跨站伪造请求。举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造的。

    Django为用户实现跨站请求伪造保护的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局设置和局部设置。


二、Django提供CSRF防护机制

    Django第一次响应某个客户端的请求时,会在服务器端随机生成一个token,然后把这个token放到cookie中返回给客户端。然后客户端每次POST请求时都会带上这个token,这样就避免了CSRF***。

  • 在返回的HTTP响应的cookie中,Django会添加一个csrftoken字段,值为随机生成的token

  • 在POST表单中,必须包含一个csrfmiddlewaretoken隐藏字段,需要在模板中添加{% csrf_token %}自动生成

  • 在处理POST请求之前,Django会验证cookie中csrftoken和表单中csrfmiddlewaretoken的值是否一致。如果一致,则表明这是一个合法请求。否则这个请求就是伪造的,返回403 Forbidden。

  • 在Ajax POST请求中,添加一个"X-CSRFToken"头部,值为cookie中的csrftoken的值。


三、CSRF设置

1. 全局设置(中间件)

django.middleware.csrf.CsrfViewMiddleware

2. 局部设置(视图函数)

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

@csrf_protect  # 为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置CSRF中间件。
def func():
    pass
    
@csrf_exempt  # 取消当前函数防跨站请求伪造功能,即便settings中设置了CSRF中间件。
def func():
    pass


四、应用CSRF

1. 前端Form表单中设置CSRF

<form method="post">
{% csrf_token %}
    ...
</form>

2. 自动在每个Ajax请求头部中添加"X-CSRFToken"

// 导入jquery.cookie.js 通过$.cookie('csrftoken')获取csrf_token
// beforeSend在每个Ajax请求之前自动设置请求头部"X-CSRFToken"

<script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
<script src="/static/plugin/jquery/jquery.cookie.js"></script>
<script type="text/javascript">
	var csrftoken = $.cookie('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);
			}
		}
	});
</script>

3. 单独在Ajax请求中设置"X-CSRFToken"头部

<script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
<script src="/static/plugin/jquery/jquery.cookie.js"></script>
<script type="text/javascript">
	$.ajax({
	    headers:{"X-CSRFToken":$.cookie('csrftoken')},
	    ...
        })
</script>