django 之 CSRF

孤街浪徒 提交于 2020-12-18 00:57:27

CSRF 即:(Cross-site request forgery)跨站请求伪造 , 是一种对网站的恶意利用,通过伪装来自受信任用户的请求来利用受信任的网站。

django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。引入csrf_token的站点只有站点服务器自身可以解码

在settings.py中开启中间件:

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

 

当开启该中间件时,在向网站提交数据如果没有携带csrftoken,表示不是通过访问我的网站提交数据的,因此csrf验证失败,将出现如下页面:

 

 

示例1:通过普通Form表单提交POST请求时在页面引入csrf_token

===login.html===

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/test/login/" method="POST">
        {% csrf_token %}
        <input type="text" name="username" placeholder="用户名" />
        <input type="password" name="pwd" placeholder="密码" />
        <input type="checkbox" name="remember"> 10秒免登陆
        <input type="submit" />       
    </form>
</body>
</html>

=== view.py ===

from django.shortcuts import render,redirect

user_info = {
    'test': {'pwd': "123123"},
}

def login(request):
    if request.method=="GET":
        return render(request,'login.html')
    elif request.method=="POST":
        u=request.POST.get('username')
        p=request.POST.get('pwd')
        print(u,p)
        dic = user_info.get(u)
        if not dic:
            return render(request,'login.html')
        if dic['pwd'] == p:
            request.session['username']=u
            request.session['is_login']=True
            print(request.POST.get('remember'))
            if request.POST.get('remember',None)=='on':
                request.session.set_expiry(10) 
            return redirect('/test/index/')
        else:
            return render(request,'login.html')

注:http://127.0.0.1:8000/test/login/ 访问发现多了一个隐藏的csrftoken <input>标签

 

csrftoken实际上保存在cookie中,可以通过 $.cookie('csrftoken') 获取 

 

 

 

示例2:通过Ajax提交POST请求在头部加入 X-CSRFtoken

=== login.html ===

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form>
        <!-- <input type="text" name="username" placeholder="用户名" />
        <input type="password" name="pwd" placeholder="密码" /> -->
        <input type="checkbox" name="remember"> 10秒免登陆
        <input id="btn1" type="button" value="Ajax_submit" />  
    </form>

    <script src="/static/jquery-1.12.4.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <script>
        $(function(){
            $.ajaxSetup({ // 给当前页面所有的ajax请求都设置上X-CSRFtoken
                beforeSend: function(xhr,settings){  // beforeSend表示在ajax操作之前先进行设置,xhr: XMLHttpRequest对象,setttings表示$.ajax({})中的所有设置
                    xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken'));
                }
            });

            $('#btn1').click(function () {
                $.ajax({
                    url: '/test/login/',
                    type:"POST",
                    data: {'username': 'test', 'pwd': '123123'},
                    // headers: {'X-CSRFtoken': $.cookie('csrftoken')}, // 如果当前页面只有一个ajax请求,只需加上这行即可,无需$.ajaxSetup
                    success: function(data){  
                        var obj = JSON.parse(data);
                        if(obj.status){
                            location.href="/test/index"; 
                        }
                    }
                })
            });
        })
    </script>
</body>
</html>

=== view.py ===

from django.shortcuts import render,redirect,HttpResponse
import json

user_info = {
    'test': {'pwd': "123123"},
}

def login(request):
    from django.conf import settings #settings表示django启动时加载到内存的默认全部配置,settings.py的变量会覆盖里面的值
    print(settings.CSRF_HEADER_NAME) #HTTP_X_CSRFTOKEN 、X-CSRFtoken 
       
    ret = {'status':True, 'error':None, 'data': None}
    if request.method=="GET":
        return render(request,'login.html')
    elif request.method=="POST":
        u=request.POST.get('username')
        p=request.POST.get('pwd')
        print(u,p)
        dic = user_info.get(u)
        if not dic:
            return render(request,'login.html')
        if dic['pwd'] == p:
            request.session['username']=u
            request.session['is_login']=True
            print(request.POST.get('remember'))
            if request.POST.get('remember',None)=='on':
                request.session.set_expiry(10) 
            return HttpResponse(json.dumps(ret))
        else:
            return render(request,'login.html') 


拓展:如果一些请求方法无需CSRF防护,可以做进一步优化 如下

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); //匹配到的这些方法不需要CSRF防护
}

$.ajaxSetup({
  beforeSend: function(xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});

 

局部配置CSRF

在settings中开启csrf中间件表示在全局中应用csrf,django还支持局部配置csrf。如views中有100个函数,其中只有两个需要开启或不需要开启csrf防护,则使用局部配置

场景一: settings中设置了全局中间件

from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def func(request):
    pass

场景二:settings中没有设置全局中间件

from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect
def func(request):
    pass

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!