什么是 CSRF
CSRF, Cross Site Request Forgery, 跨站点伪造请求。举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造的。
csrf原理
csrf要求发送post,put或delete请求的时候,是先以get方式发送请求,服务端响应时会分配一个随机字符串给客户端,客户端第二次发送post,put或delete请求时携带上次分配的随机字符串到服务端进行校验
当我们写好某个路由,需要利用post请求发送ajax到后台的时候,会发现,报出如下警告。
然后浏览器会有如下错误:
这是因为在post请求时,我们没有加CsrfToken字段,而在Django项目中,这字段相当于提交数据的令牌,是必须的。如果在开发阶段可以直接把它注释掉,这样就不会报错。
如果项目完成之后,需要开启这个中间件,又该如何解决报错的问题呢。
此时又到了我们紧张刺激的看源码时候了。
先看下图:
该字段是不是和上面的WARNINGN提示的错误文本很相似呢。
而我们的浏览器飘红报错又是出自哪里呢?
该框中的:REASON_NO_CSRF_COOKIE貌似在告诉我们,报错的原因是你的请求里没有带上CSRF cookie;
我们可以怎样获取cookie值呢?
有个很简单的办法,在浏览器中按下F12,进入开发模式。
然后输入命令:javascript:alert(document.cookie);浏览器便会把当前网页的cookie值以弹窗的方式发出来。
但是在没有注释掉csrf中间件的时候,我们利用这个命令,会发现它弹出的窗口有个cookie值
该cookie值的key是csrftoken.这时如果cookie中携带的有其他内容(以分号分隔),就要对cookie进行处理,比如说百度翻译的cookie值:
这时,在js里面写个getCookie方法,用于整理cookie,以获取csrf验证用的csrfToken:
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
let cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = jQuery.trim(cookies[i]);
// 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;
}
然后获取所需字段就行了。
另外还需知道发送到后端的Ajax请求还需携带的csrf令牌的字段名,而这在cookie中是看不到的。
这时先看看源码吧。
这是对应的csrf中间件。
当用ajax发送请求时,请求头需要携带上的字段名是:‘X-CSRFToken’,而它的值对应的是cookie里面csrftoken的值。
所以我们需要在Ajax请求头加上。
然后在需要POST请求的表单元素里面加上:
{% csrf_token %}
但是这种写法需要在每个表单都加上{% csrf_token %}
还有一种方法,不需要为每个表单加上{% csrf_token %},那就是自定义一个中间件。(中间件是介于request 和 response 处理之间的一道处理过程,用于全局范围改变Django的输入和输出,简单的来说中间件是帮助我们在视图函数指向之前和执行之后都可以做一些额外的操作)
首先找到原本用于csrf认证的类:
浏览器发送post请求,该类可用于处理请求,找到该函数如下:
注意它上面还有个get_token函数:
在自定义的子类中,重写的处理请求函数,利用get_token函数返回post表单的CSRF令牌,交给process_request进行处理。
然后再注册中间件就可以了
from django.middleware.csrf import get_token
from django.utils.deprecation import MiddlewareMixin
# 继承MiddlewareMixin类
class Middleware(MiddlewareMixin):
# 重写函数处理请求
def process_request(self, request):
# 调用包里的get_token函数,拿到令牌
get_token(request)
随后再设置文件中注册中间件即可。
补充
在正确配置以上信息的情况下
还会引起csrf报错的其他原因:
在利用Post请求提交表单的时候,如果利用submit方法的话,需选取的是整个表单元素,如果想选取某个按钮利用click方法进行Post请求提交表单的话,只需选取按钮元素就行。如果submit方法选取的不是整个表单元素,而是选了提交按钮的话,也会报csrf丢失或不正确的错误。