Django自身安全机制-XSS和CSRF
XSS攻击
什么是XSS攻击
实施XSS攻击需要具备两个条件:
一、需要向web页面注入恶意代码;
二、这些恶意代码能够被浏览器成功的执行。
解决办法:
1、一种方法是在表单提交或者url参数传递前,对需要的参数进行过滤。
2、在后台对从数据库获取的字符串数据进行过滤,判断关键字。
3、设置安全机制。
django框架:内部机制默认阻止了。它会判定传入的字符串是不安全的,就不会渲染而以字符串的形式显示。如果手贱写了safe,那就危险了,若想使用safe,那就必须在后台对要渲染的字符串做过滤了。所以在开发的时候,一定要慎用安全机制。尤其是对用户可以提交的并能渲染的内容!!!
- 示例:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form method="POST" action="/comment/"> 9 <h4>评论</h4> 10 <input type="text" name="content"/> 11 <input type="submit" value="提交" />{{ error }} 12 </form> 13 </body> 14 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <h1>评论内容</h1> 9 {% for item in msg %} 10 <div>{{ item|safe }}</div> 11 {% endfor %} 12 </body> 13 </html>
1 from django.shortcuts import render 2 msg = [] 3 def comment(request): 4 if request.method == "GET": 5 return render(request,'comment.html') 6 else: 7 v = request.POST.get('content') 8 if "script" in v: 9 return render(request,'comment.html',{'error': '小比崽子还黑我'}) 10 else: 11 msg.append(v) 12 return render(request,'comment.html') 13 14 def index(request): 15 return render(request,'index.html',{'msg':msg}) 16 17 def test(request): 18 from django.utils.safestring import mark_safe 19 temp = "<a href='http://www.baidu.com'>百度</a>" 20 newtemp = mark_safe(temp) 21 return render(request,'test.html',{'temp':newtemp}
CSRF跨站请求伪造
什么是 CSRF
CSRF, Cross Site Request Forgery, 跨站点请求伪造。举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造的。
csrf攻击过程
1.用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3.用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
csrf的攻击之所以会成功是因为服务器端身份验证机制可以通过Cookie保证一个请求是来自于某个用户的浏览器,但无法保证该请求是用户允许的。因此,预防csrf攻击简单可行的方法就是在客户端网页上添加随机数,在服务器端进行随机数验证,以确保该请求是用户允许的。Django也是通过这个方法来防御csrf攻击的。
在django防御csrf攻击
原理
在客户端页面上添加csrftoken, 服务器端进行验证,服务器端验证的工作通过'django.middleware.csrf.CsrfViewMiddleware'这个中间层来完成。在django当中防御csrf攻击的方式有两种, 1.在表单当中附加csrftoken 2.通过request请求中添加X-CSRFToken请求头。注意:Django默认对所有的POST请求都进行csrftoken验证,若验证失败则403错误侍候。
在表单中附加csrftoken
后端
1 from django.shortcuts import render 2 from django.template.context_processors import csrf 3 4 def ajax_demo(request): 5 # csrf(request)构造出{‘csrf_token’: token} 6 return render(request, 'post_demo.html', csrf(request))
前端
1 $('#send').click(function(){ 2 3 $.ajax({ 4 type: 'POST', 5 url:'{% url 'ajax:post_data' %}', 6 data: { 7 username: $('#username').val(), 8 content: $('#content').val(), 9 'csrfmiddlewaretoken': '{{ csrf_token }}' 关键点 10 }, 11 dataType: 'json', 12 success: function(data){ 13 14 }, 15 error: function(){ 16 17 } 18 19 }); 20 });
通过request请求中添加X-CSRFToken请求头
后端
该方式需要借助于Cookie传递csrftoken, 设置Cookie的方式有两种。ps:经测试即便什么都不做,也会设置Cookie,不过官方文档说,不保证每次都有效
1.表单中添加{%csrf_token%}这个模板标签
1 <form id="comment_form" action="#"></form> 2 {% csrf_token %} 就是这个 3 <p>姓名: <input type="text" name="useranme" id="username"></p> 4 <p>内容: <textarea name="content" id="content" rows="5" cols="30"></textarea></p> 5 <p><input type="button", id="send" value="提交"></p>
2.ensure_csrf_cookie装饰器
1 from django.shortcuts import render 2 from django.views.decorators.csrf import ensure_csrf_cookie 3 4 @ensure_csrf_cookie 5 def ajax_demo(request): 6 return render(request, 'ajax_demo.html')
前端要做的事情,在进行post提交时,获取Cookie当中的csrftoken并在请求中添加X-CSRFToken请求头, 该请求头的数据就是csrftoken。通过$.ajaxSetup方法设置AJAX请求的默认参数选项, 在每次ajax的POST请求时,添加X-CSRFToken请求头
1 <script type="text/javascript"> 2 $(function(){ 3 4 function getCookie(name) { 5 var cookieValue = null; 6 if (document.cookie && document.cookie != '') { 7 var cookies = document.cookie.split(';'); 8 for (var i = 0; i < cookies.length; i++) { 9 var cookie = jQuery.trim(cookies[i]); 10 // Does this cookie string begin with the name we want? 11 if (cookie.substring(0, name.length + 1) == (name + '=')) { 12 cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 13 break; 14 } 15 } 16 } 17 return cookieValue; 18 } 19 20 <!--获取csrftoken--> 21 var csrftoken = getCookie('csrftoken'); 22 console.log(csrftoken); 23 24 //Ajax call 25 function csrfSafeMethod(method) { 26 // these HTTP methods do not require CSRF protection 27 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 28 } 29 30 $.ajaxSetup({ 31 crossDomain: false, // obviates need for sameOrigin test 32 //请求前触发 33 beforeSend: function(xhr, settings) { 34 if (!csrfSafeMethod(settings.type)) { 35 xhr.setRequestHeader("X-CSRFToken", csrftoken); 36 } 37 } 38 }); 39 40 $('#send').click(function(){ 41 console.log($("#comment_form").serialize()); 42 43 $.ajax({ 44 type: 'POST', 45 url:'{% url 'ajax:post_data' %}', 46 data: { 47 username: $('#username').val(), 48 content: $('#content').val(), 49 //'csrfmiddlewaretoken': '{{ csrf_token }}' 50 }, 51 dataType: 'json', 52 success: function(data){ 53 54 55 }, 56 error: function(){ 57 58 } 59 60 }); 61 }); 62 63 64 }); 65 </script>
取消csrftoken验证
通过csrf_exempt, 来取消csrftoken验证,方式有两种。
1 .在视图函数当中添加csrf_exempt装饰器
1 from django.views.decorators.csrf import csrf_exempt 2 3 @csrf_exempt 4 def post_data(request): 5 pass
2 .在urlconf当中
1 from django.views.decorators.csrf import csrf_exempt 2 urlpatterns = [ 3 url(r'^post/get_data/$', csrf_exempt(post_data), name='post_data'), 4 5 ]
django csrf注意点:
注意一定注意:ajax POST提交的时候,csrf-token 随机字符串 直接放在data数据中的方式为:data:{csrfmiddlewaretoken:"{{ csrf_token }}"}
若是导入自己写的JS文件,那上述方法就不能获取到Django后台发送的随机字符串,而是需要利用上面介绍的两种方式获取(页面写上{% csrf_token %},通过隐藏的input标签取value值写在POST提交的data数据中;或是从cookie中获取,写在头文件中。)
使用django框架时:
每次初始化一个项目时都要看看 django.middleware.csrf.CsrfViewMiddleware 这个中间件
每次在模板里写 form 时都需要加一个 {% csrf_token %} tag
每次发 ajax POST 请求,都需要加一个 X_CSRFTOKEN 的 header
流程:
用户第一次访问页面,肯定是get请求,此时服务端就会给客户端发送一段随机字符串的数据,当客户提交数据的时候,常在POST请求中带回,就会发送随机字符串给服务端(上一次请求获取的数据)用于验证。
服务端给客户端发送的随机字符串作为一种安全机制,客户端往服务端发送请求时再携带回来,用于匹配认证。
可以从form表单中接收,也可以在cookies中查看,注意:这两个的随机字符串是不同的!
如果开启了csrf认证,后台没有拿到对应的字符串的话就会报错---> 403