通过分析中间件 django\middleware\csrf.py 中的 process_view 的流程:
-
在 settings.py 中 ‘django.middleware.csrf.CsrfViewMiddleware’ 中间件被启用,才会走到上述文件
-
读取 请求中的 cookie 的 settings.CSRF_COOKIE_NAME 即 csrftoken ,赋值给 csrf_token
-
尝试读取 请求中的 csrfmiddlewaretoken 赋值给 request_csrf_token:
request_csrf_token = request.POST.get(‘csrfmiddlewaretoken’, ‘’)
(这是为了能处理: 通过模板渲染的表单提交 POST 请求的情况)
如果没读取到,则会读取请求头中的 HTTP_X_CSRFTOKEN (这是为了能处理: 通过 js 代码,发送 POST 请求的情况)
注:Django 框架会把原请求头中的 X-CSRFToken 封装为 HTTP_X_CSRFTOKEN -
然后比较 request_csrf_token 和 csrf_token 是否一样,一样的话就可以通过 crsf 的检查。
结论:说白了比较的就是比较 (cookie 中 csrftoken 的值) 和 (纯表单提交中的csrfmiddlewaretoken 或 JS 发的请求头中的 X-CSRFToken)的值是否一样,
整个过程根本没有和数据库中的什么比较。
所以前后端分离后即后端没有了模板渲染, 如果前端要发 POST 请求(指的是 js 发 POST 请求),
需要前端工程师手动的在请求头中添加 X-CSRFToken,对应的值通过 js 读 cookie 取到
基于上述结论:你完全可以通过代码构造请求来满足上述的检查,而请求头的 cookie 中的 csrftoken 和 和请求头的 X-CSRFToken 的值可以随意填写
url = "http://192.168.56.101:8082/data/student_data/"
input_dict = {'name':'cheng', 'age':23}
res = requests.post(url, data=input_dict,
# headers={'X-CSRFToken':'F0S0sHIXOAF9pjwpn78EB5ZjxvOO4c7I','Cookie': 'csrftoken=F0S0sHIXOAF9pjwpn78EB5ZjxvOO4c7I'})
headers={'X-CSRFToken':'mycsrftokenabc','Cookie': 'csrftoken=mycsrftokenabc'})
print 'type(res.headers) = {0}, res.headers = {1}'.format(type(res.headers), res.headers)
print 'type(res.text) = {0}, res.text = {1}'.format(type(res.text), res.text)
print 'res.status_code = {0}'.format(res.status_code)
在了解了 Django 框架的 CSRF 的认证机制后,来谈一下该机制怎么规避 CSRF 攻击:
CSRF攻击攻击原理及过程如下:
- 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
- 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
- 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
- 网站B接收到用户请求后,返回一些攻击性代码(更准确的说是静态资源 < img src=“http://domain A/eg_tulip.jpg” />),并发出一个请求要求访问第三方站点A;
- 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
注意看第 5 步:
攻击代码携带网站 A 的 cookie 信息向网站 A 发送请求(因为攻击代码是运行在浏览器里的,所以当向网站A 发送请求时自然会携带上网站 A 的 cookie即携带了 sessionid 和 crsftoken):
如果在 Django 的后端使能了 django.middleware.csrf.CsrfViewMiddleware 中间件,则该请求就会被终止(虽然 sessionid 是准确的)。
因为 POST 请求中没有携带 csrfmiddlewaretoken ,而请求头的 cookie 中带有 csrftoken。
所以后端获取不到 csrfmiddlewaretoken;然后尝试去请求头中获取 HTTP_X_CSRFTOKEN ,也没有获取到值,
所以自然和 cookie 中的 crsftoken 比较就会失败即请求失败!
你可能会说:我直接手动代码模拟 POST 请求(像上面的 demo 一样,在请求头中写入自己随意编写的 crsftoken 和 HTTP_X_CSRFTOKEN),这样请求也会失败,因为你这样操作虽然能避开 django.middleware.csrf.CsrfViewMiddleware 中间件的检查,但是由于请求中没有 sessionid (因为你的代码是运行在本地的,所以没办法知道网站 A 的 sessionid 是什么。当然假设你不能 F12 查看合法用户访问网站 A时的浏览器), 所以会失败在 session 验证这一步。
也许你会想过有没有一种办法可以让 js 代码把 网站 A 的 cookie 里的内容,发给网站 B?其实是没有的,这是因为浏览器层面有隔离。
所以现在看起来 Django 框架还是很严谨的哦,通过在浏览器的 cookie 中写入 crsftoken 和 sessionid 双认证来确保网站的安全。