zoukankan      html  css  js  c++  java
  • Django csrf校验

     引入:

    通常,钓鱼网站本质是本质搭建一个跟正常网站一模一样的页面,用户在该页面上完成转账功能

    转账的请求确实是朝着正常网站的服务端提交,唯一不同的在于收款账户人不同。

    如果想模拟一个钓鱼网站,就可是给用户书写一个form表单 对方账户的input框没有name属性,然后你自己悄悄提前写好了一个具有默认的并且是隐藏的具有name属性的input框。

    如果想解决这个问题,当转账请求发送给服务端后,服务端会给各台机器返回一个随机实时字符串。下一次,如果还有请求向服务端发时,服务端会校验字符串,若对不上的话服务端就拒绝访问。这就是csrf校验。

    那么form表单如何进行csrf校验呢?

    你只需要在你的form表单内写一个{% csrf_token %}就可以了

    Ajax请求设置csrf_token的三种方式

     示例:

    urls.py

    urlpatterns = [
        url(r'^transfer/', views.transfer),
    ]

    settings.py

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]

    第三种方式的js文件(官方文档套用就行了)

     myfile.js

    function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
    var cookies = document.cookie.split(';');
    for (var i = 0; i < cookies.length; i++) {
    var cookie = jQuery.trim(cookies[i]);
    // Does this cookie string begin with the name we want?
    if (cookie.substring(0, name.length + 1) === (name + '=')) {
    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
    break;
    }
    }
    }
    return cookieValue;
    }
    var csrftoken = getCookie('csrftoken');

    function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    myfile.js

    views.py

    复制代码
    def transfer(request):
        if request.method =='POST':
            username = request.POST.get('username')
            target_user = request.POST.get('target_user')
            money = request.POST.get('money')
            print('%s 给 %s 转账 %s元' %(username,target_user,money))
        return render(request,'transfer.html')
    复制代码

    前端页面

     transfer.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
    </form>
    <button id="d1">发送ajax请求</button>


    {% load static %}
    <script src="{% static 'myfile.js' %}"></script>
    <script>
    $('#d1').click(function () {
    $.ajax({
    url:'',
    type:'post',
    // 第一种方式 自己手动获取
    {#data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},#}
    // 第二种方式 利用模板语法
    {#data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
    // 第三种 通用方式 引入外部js文件
    data:{'username':'hank'},
    success:function (data) {
    alert(data)
    }
    })
    })
    </script>
    </body>
    </html>

    transfer.html

    csrf装饰器

    csrf装饰器作用在FBV上

    装饰器模块导入:

    from django.views.decorators.csrf import csrf_exempt,csrf_protect

    当我们网站整体都校验csrf的时候 我想让某几个视图函数不校验

    @csrf_exempt #给哪个视图函数加上,就不给哪个视图校验csrf

    当我们网站整体都不校验csrf的时候 我想让某几个视图函数校验

    @csrf_protect  #给哪个视图函数加上,就给哪个视图校验csrf

    注意:验证同时需要把'django.middleware.csrf.CsrfViewMiddleware'注销掉

    csrf装饰器作用在CBV上

    当我们网站整体都不校验csrf的时候 我想让某几个视图函数校验

    复制代码
    from django.views import View
    from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    
    
    # @method_decorator(csrf_protect,name='post')  #第二种指名道姓地给某给方法装
    class MyHome(View):
        @method_decorator(csrf_protect)  #第三种 给类中所有的方法都装
        def dispatch(self, request, *args, **kwargs):
            return super().dispatch(request,*args,**kwargs)
    
        def get(self,request):
            return HttpResponse('get')
        # @method_decorator(csrf_protect)   #第一种方式
        def post(self,request):
            return HttpResponse('post')

    注意:验证同时需要把'django.middleware.csrf.CsrfViewMiddleware'注销掉
    复制代码

    当我们网站整体都校验csrf的时候 我想让某几个视图函数不校验

     总结:给CBV加装饰器 推荐使用模块method_decorator

    csrf_exempt 只能给dispatch方法装

     补充内容:

    详述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存储在哪里才能保证验证通过呢。直到看到源码。   
    def _compare_salted_tokens(request_csrf_token, csrf_token): # Assume both arguments are sanitized -- that is, strings of # length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS. return constant_time_compare( _unsalt_cipher_token(request_csrf_token), _unsalt_cipher_token(csrf_token), ) def _unsalt_cipher_token(token): """ Given a token (assumed to be a string of CSRF_ALLOWED_CHARS, of length CSRF_TOKEN_LENGTH, and that its first half is a salt), use it to decrypt the second half to produce the original secret. """ salt = token[:CSRF_SECRET_LENGTH] token = token[CSRF_SECRET_LENGTH:] chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in salt)) secret = ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok return secret        token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。     django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。     同样也不难解释,为什么ajax请求时,需要从cookie中拿取token添加到请求头中。 Cookies Hashing:每一个表单请求中都加入随机的Cookie,由于网站中存在XSS漏洞而被偷窃的危险。 HTTP refer:可以对服务器获得的请求来路进行欺骗以使得他们看起来合法,这种方法不能够有效防止攻击。 验证码:用户提交的每一个表单中使用一个随机验证码,让用户在文本框中填写图片上的随机字符串,并且在提交表单后对其进行检测。 令牌Token:一次性令牌在完成他们的工作后将被销毁,比较安全。 ...等等吧,还有很多其他的。      $.ajax({ url: "/cookie_ajax/", type: "POST", headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 从Cookie取csrftoken,并设置到请求头中 data: {"username": "chao", "password": 123456}, success: function (data) { console.log(data); } })     或者用自己写一个getCookie方法: function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken');     每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置。 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); } } });     注意:       如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。       如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。       这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。 django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def login(request): pass     更多细节详见:Djagno官方文档中关于CSRF的内容
    csrf  跨站请求伪造
    
    ~~~
    ajax({
        
        data:{csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val(),
                // csrfmiddlewaretoken:'{{ csrf_token }}',
            }
    })
    ajax({
        
        url:'/test/',
        type:'post',
        headers:{
            "X-CSRFToken": $.cookie('csrftoken'),
        },
    })
    
    
    ~~~
    
    jquery操作cookie,文件地址<http://plugins.jquery.com/cookie/>
    
    <https://www.cnblogs.com/clschao/articles/10480029.html>
    
    ~~~
    
    <script src="{% static 'jquery.js' %}"></script>
    <script src="{% static 'jquery.cookie.js' %}"></script>
    
    ~~~
    
    同源机制
    
    ~~~
    浏览器的一个安全机制,非同源不允许直接互相访问,同源:协议,域名(ip地址),端口号三项相同才是同源
    
    简单请求和复杂请求
    简单请求:
    (1) 请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求)
        HEAD
        GET
        POST
    (2)HTTP的头信息不超出以下几种字段:(如果比这些请求头多,那么一定是非简单请求)
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。
    
    简单请求只发送一次请求
    
    
    复杂请求(非简单请求) 发送两次请求
    
    
    * 简单请求和非简单请求的区别?
    
       简单请求:一次请求
       非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
    * 关于“预检”
    
    - 请求方式:OPTIONS
    - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
    - 如何“预检”
         => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
            Access-Control-Request-Method
         => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
            Access-Control-Request-Headers
    
    
    请求的网站响应内容:
    def index(request):
        a = {'name':'chao'}
        ret = JsonResponse(a)
        ret["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000" #让http://127.0.0.1:8000这个网址的所有请求都能通过同源机制获得我给他响应的数据
        ret["Access-Control-Allow-Origin"] = "*"
        ret["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000,http://127.0.0.1:8001,"
        ret["Access-Control-Allow-Headers"] = "content-type" #让所有的请求数据格式都能通过同源机制,
        return ret
    
    
    
    ~~~
    <https://www.cnblogs.com/clschao/articles/10480029.html>
  • 相关阅读:
    洛谷P1157----组合数的输出
    NOIP幂次方
    NOIP2012----借教室
    SpringBoot+Spring常用注解总结
    Spring常见问题总结
    Java 命名之道
    Redis 常见问题总结
    关于缓存的一些重要概念(Redis 前置菜)
    MySQL 高性能优化规范建议
    关于数据库中如何存储时间的一点思考
  • 原文地址:https://www.cnblogs.com/wddxx/p/13813439.html
Copyright © 2011-2022 走看看