21)django-csrf(跨站请求伪造)
一:目录
1)简介及无CSRF时存在隐患
2)csrf原理
3)csrf 设置
4)Form提交(CSRF)
5)Ajax提交(CSRF)
CSRF请求头 X-CSRFToken
二:csrf简介
csrf主要是为了防止跨站请求伪造(XSS攻击别人可以提交运行JS代码)
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。
而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
-
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
二:csrf原理
客户端发请求,如果是第一次过来,比如GET,我不仅把数据数据你,还给一串加密串,只我自己能反解。
你下一次过来提交数据的时候,你要带这串来,这样我才允许,不然允许提交数据。
这个一般体现在那里?如果你往后台发POST请求,你把CSRF禁掉话,会报403名错误了。
django做了一层防护,GET不会有影响,POST的时候会在来你发过来的请求你找那个随机字符串。
但是我们没有这个字符串。所以禁止。
我们可以在页面form请求加一
{{csrf_token}}生成随机串 这个就是CSRF的token.这样显示是没有用的。
在form里{%csrf_token%}后台才能拿到token 这是django csft原理。
比如:别人的网站写JS代码,但是提交了我们的网站,如果没有开启csrf他就直接提交过来了,并运行了代码,这是很危险的。
如果开吂CSRF。别人的网站没有随机token.他就不能提交代码到我们的网站。
三:csrf设置
settings: 'django.middleware.csrf.CsrfViewMiddleware', form表单 <form action="/login/" method="post"> {% csrf_token %} <!--post提供token给后台--> <input type="text" name="username"> <input type="text" name="password"> <input type="checkbox" value="1" name="rmb">免登录10秒 <input type="submit" name="+"> </form>
# {% csrf_token %}不仅在页面中生成,在cookie中也有生成.如图所示
四:form提交数据
上面的提交需要csft_token传入到后台。我们有两种方式提交。第一种:普通submit,;第二种ajax提交。ajax也要带token,这能提交数据到后台
普通submit提交,只需要在form里设置
{% csrf_token %}
提交POST后页面和cookie效果,会生成token:
<form action="/login/" method="post"> <input type='hidden' name='csrfmiddlewaretoken' value='GGO5hq95CfY3PrbGU2wep1WOpbMZOYNJ02QunbqKQOmeh1ShXNC1IIiTbut1Yij2' /> <input type="text" name="username"> <input type="text" name="password"> <input type="checkbox" value="1" name="rmb">免登录10秒 <input type="submit" name="+"> </form> </body> </html>
浏览器cookie Request Cookies 212 Hm_lpvt_407473d433e871de861cf818aa1405a1 1510386206 N/A N/A N/A 53 Hm_lvt_407473d433e871de861cf818aa1405a1 1509847957,1509936833,1509966824,1510386206 N/A N/A N/A 85 csrftoken 4MGKIn5K2p8K3hYkPKPagY0akHpjieqZo8I9O8mpgYwVvRFVSvVXzFmf606lsyWi N/A N/A N/A 74 Response Cookies 139 csrftoken 4MGKIn5K2p8K3hYkPKPagY0akHpjieqZo8I9O8mpgYwVvRFVSvVXzFmf606lsyWi / 364.0 days 139
五:ajax提交
AJAX只要把csrftoken拿过来,放在请求头发过去就行了
django里token的KEY是什么:X-CSRFtoke
from django.conf import settings print(settings.CSRF_HEADER_NAME)===>HTTP_X_CSRFTOKEN(jdango会在前面加HTTP_)==>X-CSRFtoken(请求头不能加下划线) 按django规范来写就是X-CSRFtoken
ajax提交两种方法:
1)在ajax里提交headers:{"X-CSRFtoken":csrftoken},但是如果提交ajax非常多的时候就不方便,可以设置全局提交。
<script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $("#btn").click( function(){ //获取token var csrftoken=$.cookie("csrftoken") $.ajax({ url:"/login/", type:"POST", //token放在请求头 headers:{"X-CSRFtoken":csrftoken}, data:{"username":"root","password":123}, success:function(data){ } }) }) </script>
2)全文提交
#方式2 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> {% csrf_token %} <input type="text" name="username"> <input type="text" name="password"> <input type="checkbox" value="1" name="rmb">免登录10秒 <input type="submit" name="+"> <input id="btn" type="button" value="增加"> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $("#btn").click( function(){ //这就是发ajax请求 {# obj=XMLHttpRequest()#} {# obj.open()#} {# obj.send()#} //可以对整个页面所有的ajax请求做配置 //方式2 $.ajaxSetup({ //表示在发送ajax请求之前执行,这是全局中配置,比如下面有好多个ajax 请求 beforeSend:function(xhr,settings){ //xhr表示XML HTTP requst对象,所有AJAZ操作 xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken")) } }) //获取token {# var csrftoken=$.cookie("csrftoken")#} $.ajax({ url:"/login/", type:"POST", //token放在请求头 //方式1 {# headers:{"X-CSRFtoken":$.cookie("csrftoken")#} data:{"username":"root","password":123}, success:function(data){ } }) }) </script> </body> </html>
3)其他问题
A)
#现在是全局都加了token,如果有几个就不需要token验证,如何处理? 给不需要验证的请求加@csrf_exempt装饰器
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
B)GET是不需要提交token认证的,所以修改如下:
#上面POST和GET都是token验证,其实GET是不需要token的,如何处理? <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> {% csrf_token %} <input type="button" onclick="Do();" value="Do it"/> <script src="/static/plugin/jquery/jquery-1.8.0.js"></script> <script src="/static/plugin/jquery/jquery.cookie.js"></script> <script type="text/javascript"> var csrftoken = $.cookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); function Do(){ $.ajax({ url:"/app01/test/", data:{id:1}, type:'POST', success:function(data){ console.log(data); } }); } </script> </body> </html>
总结:
1)csrf主要是在settings里面设置,这个设置是全局的,特别设置可以使用@csrf_protect装饰器,即使全局中没有开启csrf验证,也要求验证。@csrf_exempt装饰器,可以要求不需要验证
2)在页面form设置{% csrf_token %}
3) 普通提交,后台不需要做任务修改
4)ajax提交,需要把token发送到后台,后台也不需要做任务修改