django的crsf机制防御详解及在前后端分离中post数据到django
CSRF(Cross Site Request Forgery) 跨站点伪造请求
某个用户已经登陆了你的网站,另外有一个恶意的网站有一个指向你网站的链接,那么当用户点击这个链接时,就会请求你的网站,但是你的网站以为是用户发来的请求,这时恶意网站就得逞了。
django的应对措施
用户在post请求时,发送给用户一个token,然后在django内部实现了一个校验这个token的函数,当正确时,服务器就会返回正确的内容,如果不正确就是返回403。
django自带的表单模版中可以直接使{% csrf_token %}来通过django的检验。
如何工作
引用django官网 点此查看
CSRF保护基于以下内容:
- CSRF cookie,基于随机密钥值,其他站点无权访问。
这个cookie设置为CsrfViewMiddleware。django.middleware.csrf.get_token()如果尚未在请求中设置,则与每个已调用的响应(内部用于检索CSRF令牌的函数)一起发送。
为了防止BREACH攻击,令牌不仅仅是秘密; 随机盐被置于秘密之前并用于加扰它。
出于安全原因,每次用户登录时都会更改密钥的值。
-
所有传出POST表单中都有一个名为“csrfmiddlewaretoken”的隐藏表单字段。此字段的值同样是秘密的值,盐添加到它并用于加扰它。每次调用时都会重新生成salt,get_token()以便在每个此类响应中更改表单字段值。
这部分由模板标签完成。 -
对于未使用HTTP GET,HEAD,OPTIONS或TRACE的所有传入请求,必须存在CSRF cookie,并且’csrfmiddlewaretoken’字段必须存在且正确。如果不是,用户将收到403错误。
验证’csrfmiddlewaretoken’字段值时,只将秘密而不是完整令牌与cookie值中的秘密进行比较。这允许使用不断变化的令牌。虽然每个请求都可以使用自己的令牌,但秘密仍然是所有人共同的。
这项检查是通过CsrfViewMiddleware。 -
此外,对于HTTPS请求,严格的引用检查由 CsrfViewMiddleware。这意味着即使子域可以在您的域上设置或修改cookie,它也不能强制用户发布到您的应用程序,因为该请求不会来自您自己的确切域。
这也解决了在使用会话独立秘密时在HTTPS下可能发生的中间人攻击,因为Set-Cookie即使用户在HTTPS下与某个站点通信时,HTTP 头(不幸)也被接受了。(对HTTP请求不进行引用检查,因为在HTTP下,Referer标头的存在不够可靠。)
如果CSRF_COOKIE_DOMAIN设置了该设置,则将引用者与其进行比较。此设置支持子域。例如, 将允许来自和的POST请求 。如果未设置该设置,则引用必须与HTTP 标头匹配。CSRF_COOKIE_DOMAIN = ‘.example.com’www.example.comapi.example.comHost
可以使用该CSRF_TRUSTED_ORIGINS设置将已接受的引用扩展到当前主机或cookie域之外。
请求分析
在django第一次渲染渲染模版的时候,{% csrf_token %}
会调用django.middleware.csrf.get_token
生成一个64位的无序字符csrfmiddlewaretoken
放在html里,并且会在浏览器的cookie里面设置64位的csrftoken=
,然后在用户点击提交按钮的时候会在后台通过调用某种算法来计算这两个值生成的token是否相等来方法csrf攻击
1 |
|
下面来是生成token的算法分析
django source
主要的一些算法代码
1 |
def get_token(request): |
1 |
from django.utils.crypto import get_random_string |
cookie中的值csrftokrn
在用户请求了一次后以后不会再改变,而csrfmiddlewaretoken
会每次改变
主要要用的三个函数的作用
get_token
是返回csrfmiddlewaretoken
的函数,同时第一次请求的话会设置csrftoken
到到ccookie中 ,要是重复请求则不会再重新生成。salt_cipher_secret
是生成csrfmiddlewaretoken
和csrftoken
的主要函数,此函数需要一个secret,通过这个secret然后加上salt就可以用相同的secret生成不同的64位token。unsalt_cipher_secret
是通过token来反向生成secret的函数,此方法主要用两个用途:- 重复请求时,通过cookie中的csrftoken然反向生成生成这个值的secret,然后使用这个secret来生成表单中的csrfmiddlewaretoken,
- 提交表单后通过这个函数, post数据中的
csrfmiddlewaretoken
和cookie中的csrftoken会通过这个函数计算出生成这两个值的secret如果相等,就验证成功,如果不想等就会返回403
算法分析salt_cipher_secret
生成需要加入的盐salt,然后分别计算出secret和salt在CHAR中的索引值,然后将对应的索引值相加后除以CHAR的长度后得到的余数,再在CHAR找出对应的值,然后再与salt相加返回,unsalt_cipher_secret
此函数的通过token反向得到secret与salt_cipher_secret
步骤相反,通过切片得到salt和token1,然后分别找出在CHAR中的索引值,然后相减后再在CHAR中找出对应索引上的值,然后拼接成secret。
前后端分离后前端验证django的token
只要前端在post数据的时候只要把每次通过get_token函数生成的随机字段发送过去就可以验证了,可以放在post数据中,也可以放在请求头中,
放在请求头中
django的views中设置一个可以获取csrfmiddlewaretoken
的方法就是调用get_token函数返回一个字串
django
1 |
# views.py |
前端vue
先请求这个返回token的接口,然后将返回的值加入到post放的请求,代码如下
1 |
sumbit() { |
js 使用session 、cookie、angular cookie保存token
目录
1、前言
2、js前端使用session保存token
3、js前端使用cookie保存token
4、js前端使用angular cookie保存token
内容
1、前言
在前端请求后台的API接口的时候,为了安全性,一般需要在用户登录成功之后才能发送其他请求。
所以,在用户登录成功之后,后台会返回一个token给前端,这个时候我们就需要把token暂时保存在本地,每次发送请求的时候需要在header里面带上token,这时候本地的token和后台数据库中的token进行一个验证,如果两者一致,则请求成功,否则,请求失败。
如下图,登录成功之后返回token:
如下图所示,进行其他请求的时候在header里面带上token:
2、js前端使用session保存token
sessionStorage属性允许你访问一个Session Storage对象,它与localStorage相似,不同之处在于localStorage里面存储的数据没有过期设置,而存储在sessionStorage里面的数据在页面会话结束时会被清除。页面会话在浏览器打开期间一直保持,并且重新加载或者恢复页面仍会保持原来的页面会话。
下面是session中储存token的具体的语法:
// 保存数据到sessionStorage sessionStorage.setItem('key', 'value'); // 从sessionStorage获取数据 var data = sessionStorage.getItem('key'); // 从sessionStorage删除保存的数据 sessionStorage.removeItem('key'); // 从sessionStorage删除所有保存的数据 sessionStorage.clear();
3、js前端使用cookie保存token
将token保存在cookie中,一旦浏览器关闭,cookie中的token就会被清空。
下面是cookie中存储token的语法:
//将token保存在cookie中 document.cookie=token; //从cookie中取出token var token = document.cookie.split(";")[0];
4、js前端使用angular cookie保存token
在angular中引入cookie必须首先引入angular-cookies.js文件。下面是直接能够在浏览器中打开的版本的链接:https://cdn.bootcss.com/angular-cookie/4.1.0/angular-cookie.js
下面是angular cookie中存储token的语法:
//存储token angular .module('theme.demos.login_page', ['ngCookies']) .controller('LoginController'['$scope','$cookieStore',function($scope,$cookieStore) { var valueIndex=encodeURI(data.data.token); $cookieStore.put('tokenIndex',valueIndex); }) //拿到token angular .module('theme.demos.login_page', ['ngCookies']) .controller('LoginController'['$scope','$cookieStore',function($scope,$cookieStore) { var token= $cookieStore.get('tokenIndex'); })
以上就是三种较为常见的js中存储token的方法。并且这三种方法在Google浏览器、Firefox浏览器、IE浏览器中均可兼容,但是在Safari浏览器中会出现不兼容问题(Apple),主要是无法将token写入cookie中。