CSRF
详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。
所以解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验。
那么django中csrf认证怎么玩的呢?
官方文档中说到,检验token时,只比较secret是否和cookie中的secret值一样,而不是比较整个token。
我又有疑问了,同一次登录,form表单中的token每次都会变,而cookie中的token不便,django把那个salt存储在哪里才能保证验证通过呢。直到看到源码。
1、csrf流程
1 在响应的页面中加入{% csrf_token %}标签,那么在进行模板渲染是会生成如下标签
2 并且在响应还有这个 {% csrf_token %}标签的页面时,会添加cookie键值对,如下
csrftoken:lsMQeJgVbIKKxlfz6umgYM8WOWx1Njr77cHzM0L4xtXoApsnhFXXk1OGzwb1dd0G
3 当用户从该页面提交数据时,会携带csrfmiddlewaretoken:ppwN8yg1wVEyXDxtMpVIrc4zV3gHiDKKb9rwGPLaSGRc0HKhXAwpNrKjGDUHIxjj和cookie键值对
4 取出cookie中的csrftoken值和请求数据部分的csrfmiddlewaretoken的值,两者进行比较,这个随机字符串叫做token字符串. django如果在请求数据部分找不到tokne值,会去请求数据中的请求行部分,找一个叫做X-CSRFToken的键值对,如果这个键对应的值和cookie中csrftoken对应的值相同,也能通过认证.
过程:
token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret字符串。
secret:是settings配置文件中的serect_key:
SECRET_KEY = ‘s!xbzez1zxrevmq7k_89%%-z&#)e7686pyq8pg@_bp_k_2s^ho’
django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。
如果不同就403 forbiddensecert_key:
前端页面携带的token值64位
当我们点击添加书籍时,form表单中只要添加了{%csrf_token%},页面就会自动添加个隐藏的input标签,每次请求得到的的value值都不相同
csrftoken的值不变
只要手动改下这个隐藏的input里面的value值,再提交数据就会forbidden。这个值只有是后台给的才能通过鉴权
token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。
django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。
同样也不难解释,为什么ajax请求时,需要从cookie中拿取token添加到请求头中。
Django默认对修改数据的请求强制要求csrf_token验证
提交表单的请求和响应头都带csrftoken
2、Ajax通过django的csrf_token认证机制的写法
通过form表单登录,放开csrf
那么表单那里就要添加{%csrf_token%},才能提交,不然提交报forbidden
Ajax提交也必须携带csrfmiddlewaretoken 这个键值,否则也不能提交
现在没有添加,提交
报403 forbidden,没通过认证Django回复一个认证错误的页面
三种方式提交都需要在html中添加{%csrf_token%},第一次要在浏览器中存cookie
1.方式一
只需要在html文件中添加{%csrf_token%},然后将csrfmiddlewaretoken的值提交上去就可以
注意:提交数据csrf的键值必须是csrfmiddlewaretoken
$('#btn').click(function () { {#发送ajax请求,指定请求方法,请求路径,请求携带的数据(以字典的形式携带)#} {# 先获取用户输入内容 #} var uname = $('#username').val() var pwd = $('#password').val() var token = $('[name="csrfmiddlewaretoken"]').val() $.ajax({ type: 'post', url: '/login/', data: {username: uname, password: pwd, csrfmiddlewaretoken: token}, {# 这里提交数据跟form表单提交数据是一样的,相当于form表单的name属性值 #} {# 请求成功后的触发success对应的会调函数,并且将接收到的值给了函数的变量res #} {# res接受的是请求成功之后的响应结果,如果ajax判断请求成功和失败,有后台决定 #} {# // 后台响应的状态码如果是2xx\3xx等,ajax发现应用状态为2xx\3xx等,ajax就知道请求成功了 #} success: function (res) { console.log('success', res) }, {# 请求失败的回调函数,/后台响应的状态码为4xx或者5xx表示请求失败或者服务器出问题了,没有正常响应本次请求的内容 #} {# ajax接受到响应, 如果发现响应状态码为4xx或者5xx, 那么ajax会自动触发error对应的函数#} {# // 并将响应结果传给函数作为参数(error参数 #} error: function (error) { console.log('error', error) {# 错误提示打印到页面上 #} {#$('#ajax_error').text('输入的用户名或者密码有误!');#} $('#ajax_error').text(error.responseText) } }) })
登录成功
输入错误登录
说明已经通过验证,前端post请求先过csrf认证,过了才会去找路由
2.方式二:不用js取值,直接在data中取token值
$.ajax({ type: 'post', // 请求方法. url: '/login/', // {{ csrf_token }}拿到是{% csrf_token %}生成的input标签的那个value属性的token值 data: {xname: uname, pwd: password, csrfmiddlewaretoken: '{{ csrf_token }}'}, success: function (res) { console.log('success', res); }, error: function (error) { console.log('error>>>>>', error); $('#ajax_error').text(error.responseText); } })
注意要有引号
浏览器查看,获取的就是token值
3.方式三: 添加请求头键值对,键必须叫做:‘X-CSRFToken’,值是cookie中的token值
需要借助jquery.cookie.js
$.ajax({ type: 'post', // 请求方法. url: '/login/', data: {xname: uname, pwd: password}, headers:{ // 设置请求头键值对 'X-CSRFToken':$.cookie('csrftoken'), }, success: function (res) { console.log('success', res); }, error: function (error) { console.log('error>>>>>', error); $('#ajax_error').text(error.responseText); } })
原生JS代码可以直接获取cookie
3、jquery获取cookie
定义:让网站服务器把少量数据储存到客户端的硬盘或内存,从客户端的硬盘读取数据的一种技术;
下载与引入:jquery.cookie.js基于jquery;先引入jquery,再引入:jquery.cookie.js;下载:http://plugins.jquery.com/cookie/
http://plugins.jquery.com/cookie/
https://www.bootcdn.cn/jquery-cookie/
官网,点download下载
bootcdn第三方加速网,可以复制链接将文件存到本地
引入使用,要先引入jquery
<script type="text/javascript" src="js/jquery.min.js"></script> <script type="text/javascript" src="js/jquery.cookie.js"></script>
1.cookie操作
添加cookie
$.cookie(‘the_cookie’, ‘the_value’);
这里没有指明 cookie有效时间,所创建的cookie有效期默认到用户关闭浏览器为止,所以被称为 “会话cookie(session cookie)”。
创建一个cookie并设置有效时间为 7天
$.cookie(‘the_cookie’, ‘the_value’, { expires: 7 });
这里指明了cookie有效时间,所创建的cookie被称为“持久 cookie (persistent cookie)”。注意单位是:天;
读取cookie
$.cookie(‘the_cookie’); #the_cookie是键
删除cookie
$.cookie(‘the_cookie’, null); //通过传递null作为cookie的值即可
6.可选参数
$.cookie(‘the_cookie’,‘the_value’,{
expires:7,
path:‘/’,
domain:‘jquery.com’,
secure:true
})
添加请求头键值对,键必须叫做:‘X-CSRFToken’,通过jquery 取token值,值是cookie中的token值
这红方式能使用的前提是浏览器中本来要有csrftoken这个token,不然jquery获取不到token,依然提交不了
可以正常登录
可以看到浏览器请求头中包含了X-CSRFToken键值对
总结,如上,就是全部的CSRF的实现运用原理