• 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防护的。

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

    1. from django.conf import settings
    2. from django.middleware.csrf import rotate_token, get_token
    3. rotate_token(request) # 生成csrftoken的随机字符串
    4. tokenstr = get_token(request) # 取出csrftoken字符串
    5. response.set_cookie(
    6. key=settings.CSRF_COOKIE_NAME,
    7. value=tokenstr,
    8. domain=settings.SESSION_COOKIE_DOMAIN,
    9. )
    10. 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包就可以通过

    1. import Cookies from 'js-cookie'
    2. 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自动处理。

  • 相关阅读:
    mysql之count(*)
    PyTorch - 大模型多卡训练 “CUDA error: an illegal memory access was encountered”
    数据解析——Jsonpath
    C++类型转换详细说明
    C++学习——内联函数详解
    【数据库入门到精通】mysql的存储过程实战
    Java版本+企业电子招投标系统源代码+支持二开+招投标系统+中小型企业采购供应商招投标平台
    Threejs汽车展厅
    Java学习 --- 设计模式的原型模式
    在二叉树(搜索树)中找到两个节点的最近公共祖先(剑指offer)
  • 原文地址:https://blog.csdn.net/silent_missile/article/details/127708023