Django中使用Ajax时使用CSRF保护

Django默认提供了CSRF保护模块,在settings.py的MIDDLEWARE节加上"django.middleware.csrf.CsrfViewMiddleware"就可以自动开启,而且当使用django-admin startproject project_name时会自动添加,很方便。

传统上Django会使用template来处理html页面并返回给客户端,如果是这样,那么只有当客户端采用POST、DELETE、PUT、PATCH这4中非幂等的方法访问服务器时才会激活CSRF保护中间件。而如果采用模板得到的html,这4中方法都是在表单元素中才有。而处理起来也很简单,只要在template的表单元素中增加一句{% csrf_token %}就可以自动在表单中添加隐藏的csrftoken条目,并且在表单提交时自动把csrftoken的内容合并到请求中发给服务器,服务器接到的请求中找到了csrftoken并且比对成功,CSRF就通过了。

但是当使用Ajax时,没有表单,Django也没法自动添加相关内容,就需要开发者额外处理。

服务端发放csrftoken

一般来讲,在中间件中配置CSRF中间件,Django就会对所有的访问请求开启CSRF防护,但是当一个用户初次访问网站时是没有csrftoken的,这个时候就必须要由服务器发放一个csrftoken给客户端,也就是说这个发放csrftoken的功能——比如说登录功能——是必须要使用csrf_exempt关闭该功能的CSRF防护的。

然后在登录接口中需要使用

from django.conf import settings
from django.middleware.csrf import rotate_token, get_token
rotate_token(request) # 生成csrftoken的随机字符串
tokenstr = get_token(request) # 取出csrftoken字符串
response.set_cookie(
    key=settings.CSRF_COOKIE_NAME,
    value=tokenstr,
    domain=settings.SESSION_COOKIE_DOMAIN,
)
return response

其中CSRF_COOKIE_NAME和SESSION_COOKIE_DOMAIN在settings.py中设置,具体设置方法参考

https://docs.djangoproject.com/zh-hans/4.1/ref/settings/#csrf-cookie-name

这样就可以把生成的csrftoken随机字符串通过cookie发送给客户端,cookie中可以通过CSRF_COOKIE_NAME把这个字符串读取出来。等到用户下次再访问的时候,只要把这个csrftoken通过合适的方式发送给服务器就可以让服务器的CSRF防护通过。

当然,也可以把csrftoken以其他形式加在响应内容中发给客户端,客户端根据前后端协商的接口对csrftoken做处理,等待下次访问时按照协商好的接口把csrftoken发送服务器做验证。

https://docs.djangoproject.com/zh-hans/4.1/howto/csrf/#using-csrf-protection-with-ajax

对把csrftoken存放在cookie中的安全性提出了一些质疑,不过只要调整CSRF_USE_SESSIONS设置和CSRF_COOKIE_HTTPONLY设置就可以调整好安全性。但是要记住,如果设置了CSRF_USE_SESSIONS,那么就不能通过cookie读取csrftoken。如果设置了CSRF_COOKIE_HTTPONLY就不能通过JavaScript读取csrftoken,就只能使用表单内嵌的隐藏元素来提交csrftoken了。

不管怎么说,只要浏览器设置正确,把发给客户端的csrftoken存放在cookie是安全的。

客户端取得csrftoken

客户端在发送Ajax请求之前,必须要通过JavaScript取得存在cookie里的csrftoken,比如说使用

https://github.com/js-cookie/js-cookie/

提供的js-cookie包就可以通过

import Cookies from 'js-cookie'
csrftoken_str=Cookies.get('csrftoken')

来方便的把csrftoken读取出来。

客户端把csrftoken按照符合规定的格式发送给服务器

在Header中发送

按照

How to use Django's CSRF protection | Django 文档 | Django

的说明,客户端在发送Ajax请求到服务器时需要按照以下格式设置HTTP请求的Header

'X-CSRFToken': csrftoken_str

这样就可以把csrftoken发送给服务器了,其中的key也就是X-CSRFToken来源于

https://docs.djangoproject.com/zh-hans/4.1/ref/settings/#csrf-header-name

的要求。value也就是csrftoken_str是前面JavaScript取出来的csrftoken值。

通常来讲这样服务器就可以接受了。但是实际上这里面还有浏览器做的2项额外的处理。

浏览器的额外处理

服务器同时服务大量的用户,每个用户都有一个csrftoken,所以服务器首先要知道请求者是谁,然后才能拿着这个请求中的csrftoken去对比。也就是说在提交csrftoken的同时,还要提交session的id值

这个session的id值是怎么发送给客户端的呢。类似csrftoken,这个sessionid通常也是在登录时有服务器生成,然后一并发送给客户端的。

配置 | Django 文档 | Django

说明在Django的settings.py中的SESSION_COOKIE_NAME配置了服务器在发送session的id值时采用的配置。也就是说在cookie中SESSION_COOKIE_NAME这个key对应的value就是session的id值。

通常来讲,浏览器在访问服务器时会默认把对应的域名的所有cookei都一并发送给服务器,所以这里是浏览器自动处理的,一般不需要开发者干预。但是如果需要额外干预的话,开发者可以用类似的方法把sessionid读取出来,然后在Request Header中增加相关内容发送给服务器

'sessionid':sessionid_value

另外,浏览器通常的cookie安全策略都是严格同源,也就是不同源的cookie浏览器默认是不会发送的,除非浏览器的用户做了特殊设置。同时服务器发给客户端的cookie也要设置安全策略:是否允许不同源的会话读取cookie。

https://docs.djangoproject.com/zh-hans/4.1/ref/settings/#csrf-cookie-samesite

有Django对cookie安全设置的说明,主要的两个选项是CSRF_COOKIE_SAMESITE和CSRF_COOKIE_SECURE

如果这里要求开发者手动干预的话,需要设置request选项

mode: 'same-origin' // Do not send CSRF token to another domain.

其实也就是在Request Header中增加了这么一个选项

如果是使用浏览器的话,这两项一般是不需要额外设置的。

但是如果是使用开发辅助工具模拟浏览器访问服务器的话——比如说PostMan,就需要手动设置一下。因为众所周知的原因,Google市场难以访问,所以我用Firefox浏览器上的模拟插件</>RESTD

RESTED – 下载 🦊 Firefox 扩展(zh-CN)

这样在访问有CSRF防护的url的时候,就需要在Header增加csrftoken、sessionid、mode这么3个选项才能正确访问。当然,对于Ajax访问而言,Content-Type也是必须要设置的。所以一共需要设置至少4个Header参数才能保证正确。

在request body中发送

在HTTP和HTTPS协议中,在Header中的信息都是不加密的,如果把一些保密信息比如sessionid/csrftoken这种涉及到身份认证的信息放在Header中如果被有心人抓包,是容易泄漏的。

在HTTPS协议中 ,POST的body是加密的,所以更安全的方法是把这些涉及到身份认证 的信息放在body里。

如果是用axios或者XMLHttpRequest的话,不需要任何处理。因为XMLHttpRequest默认会自动读取cookie,并在向服务器发送request的时候把cookie放在body里,而axios是对XMLHttpRequest的封装。除非手动对request body进行处理,否则cookie完全可以交给XMLHttpRequest自动处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值