django 之 CSRF
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

浙公网安备 33010602011771号