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
来源:oschina
链接:https://my.oschina.net/u/4329479/blog/4082294