Django自身安全机制-XSS和CSRF

XSS攻击

什么是XSS攻击

XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。比如这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。这种类型的漏洞由于被黑客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。对于跨站脚本攻击,黑客界共识是:跨站脚本攻击是新型的“缓冲区溢出攻击“,而JavaScript是新型的“ShellCode”。
 

实施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   });
View Code

通过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

posted @ 2018-02-07 15:44  南城思念,北城等待  阅读(225)  评论(0编辑  收藏  举报