csrf简单说明

csrf攻击流程

 

 

csrftoken机制

 

 

form表单通过csrftoken认证

login.html文件

<form action="/login/" method="post">
    {% csrf_token %} -- 会生成一个隐藏的input标签,value属性里面放着token值,name属性值为csrfmiddlewaretoken
    用户名: <input type="text" name="username">
    <input type="submit">
        
</form>

{% csrf_token %} 当我们使用form表单标签来发送请求时,如果需要通过csrftoken认证,那么必须将它写到我们的form表单标签里面,里面的任意位置

校验过程解释

html页面中的{% csrf_token %}
  <input type="hidden" name="csrfmiddlewaretoken" value="WVHKQeAuMS4RGqyLybryIBAfacDa1Dp7PEaB3Badv3y0fvLqydX36xAVen6z3oS4">
  
cookie中的 
    csrftoken:CeFG6SA8Y8hcHAX5R93sxrS37v3iFFlcvX8xjfaRHjLlgFaKRbzXVnSJbGwHHqO9
    
django会取出提交数据部分的token值和cookie中的token值进行如下比较:
    WVHKQeAuMS4RGqyLybryIBAfacDa1Dp7PEaB3Badv3y0fvLqydX36xAVen6z3oS4 -- 前32位可以对后面的几位进行解密,解密出以secret_key1  一个字符串
    
    CeFG6SA8Y8hcHAX5R93sxrS37v3iFFlcvX8xjfaRHjLlgFaKRbzXVnSJbGwHHqO9 -- 前32位可以对后面的几位进行解密,解密出以secret_key2  一个字符串
    
    secret_key1 = secret_key2 
    
    说明:
         token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。
    django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。
    
MIDDLEWARE = [
        ...
    'django.middleware.csrf.CsrfViewMiddleware',
      ...
]
    
源码
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

 

ajax请求通过csrftoken认证

方式1

首先在html文件中间,写上我们的{% csrf_token %}

$('#btn').click(function () {

        var uname = $("#uname").val();
        // 获取csrfmiddlewaretoken的input标签value属性对应的值
        var token = $('[name="csrfmiddlewaretoken"]').val();

        $.ajax({
            url:'/login/',
            type:'post',
            //将token值放到请求数据部分
            data:{uname:uname,csrfmiddlewaretoken:token},
            success:function (res) {
                console.log(res);
            }

        })
    })

方式2:

使用'{{ csrf_token }}' 这个模板渲染标签,这样就不要在我们html页面中写{% csrf_token %}了。
$('#btn').click(function () {

        //方式1
        var uname = $("#uname").val();
        // 直接就能得到csrfmiddlewaretoken的input标签value属性对应的值
        var token = '{{ csrf_token }}';
                
        $.ajax({
            url:'/login/',
            type:'post',
            data:{uname:uname,csrfmiddlewaretoken:token},
            success:function (res) {
                console.log(res);
            }

        })

    })

方式3

借助js操作cookie的方法来获取到cookie中的csrftoken那个键对应的值
然后将这个值组成一个请求头,放到此次请求中
headers:{"X-CSRFToken":这个值}
这个值,通过js能够获取,通过jquery也能获取,我们看一下jquery如何操作cookie

jquery操作cookie参考:https://www.cnblogs.com/clschao/articles/10480029.html
下载网址:http://plugins.jquery.com/cookie/
$('#btn').click(function () {

        //方式3
              // 前提:需要在html页面中写上{% csrf_token %}或者{{ csrf_token }},不然此次获取这个html页面的时候,响应中不会有这个csrftoken的cookie值 
        var uname = $("#uname").val();
        
              // 通过js或者jquery来获取cookie中的csrftoken这个键对应的token值
        var token = $.cookie('csrftoken');  
        $.ajax({
            url:'/login/',
            type:'post',
              // 将获取到的token值放到请求头中,这个请求头键值对的的键必须是"X-CSRFToken"
            headers:{
                "X-CSRFToken":token,
            },
            // django先去获取请求数据部分的token值,获取不到,就去找一个叫做X-CSRFToken请求头键值对,他的值和cookie中的csrftoken的值要相等。
            data:{uname:uname,},
            success:function (res) {
                console.log(res);    
            }

        })

    })

 

ajax上传文件

html部分

用户名: <input type="text" name="username">
密码: <input type="text" name="password">
头像: <input type="file" name="touxiang" >
<button id="sub">上传</button>

js部分
$('#sub').click(function () {

        //ajax上传文件必须依赖于FormData对象
        var formdata = new FormData();

        var uname = $('[name="username"]').val();
        var pwd = $('[name="password"]').val();
              // 获取浏览器上的文件数据方法:$('[name="touxiang"]')[0].files[0]
        var file_obj = $('[name="touxiang"]')[0].files[0];

        {#formdata.append('xx','oo')  //django 获取数据:post request.POST.get('xx') -- oo#}
        //request.POST
        formdata.append('uname',uname)
        formdata.append('pwd',pwd)
{#        {% csrf_token %}#}
        formdata.append('csrfmiddlewaretoken','{{ csrf_token }}')

        //request.FILES
        formdata.append('touxiang',file_obj)

        $.ajax({
            {#url:'/upload/',#}
            url:'',  //如果路径为空,那么使用当前页面的路径,也就是往当前页面的路径下进行提交
            type:'post',
            data:formdata,
            // 下面的两个参数的意思是,不要对数据进行任何的预处理和加工
            // 固定写法
            processData:false,
            contentType:false,

            success:function (res) {
                console.log(res);

            }

        })

    })

views页面

def login(request):
    if request.method ==  "GET":
        return render(request,"login.html")
    else:
        # file_obj = request.FILES.get('touxiang')
        # with open(file_obj.name,"wb") as fp:
        #     for line in file_obj:
        #         fp.write(line)
        # 方式二: chunks()可以设定传输文件时每次传送的大小
        file_obj = request.FILES.get('touxiang')
        with open(file_obj.name,"wb") as fp:
            for chunk in file_obj.chunks():
                fp.write(chunk)
        return HttpResponse('OK')

 

上传多文件

标签写法: <input type="file" name="touxiang" multiple>

 

posted on 2020-07-28 14:17  fdsimin  阅读(330)  评论(0编辑  收藏  举报