csrf 跨站请求伪造
一、简介
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。
1.1 第1次来访问的时候(get方法),先拿到字符串;下次再来访问的时候(post方法)也必须带着这一串字符串才能成功。CSRF是指提交数据的时候必须通过验证。cookie和session是关于用户名/密码保存的。
第2种方法
form表单提交的时候,加上它就可以了。
这个CSRF字符串不仅在表单里面有了,在cookie里面也有了。
当用form表单提交的时候,把随机字符串和cookie值都发过去了。
1.2 如果用Ajax往后台发数据的时候,只需要把cookie值拿到,放到请求头里面发过去就可以了。
先来看一个没有加cookie值的Ajax请求过程:
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="POST" > {%csrf_token%} <input type="text" name="user" placeholder="user"/> <input type="text" name="pwd" placeholder="pwd"/> <input type="checkbox" name="remember" value="1"/> 10秒免登录 <input type="submit" value="提交"/> <input id="btn" type="button" value="按钮"/> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function(){ $('#btn').click(function(){ $.ajax({ url:'/login/', type:"POST", data:{'user':'root','pwd':'123'}, success:function(arg){ } }) }) }) </script> </body> </html>
此时如果登录的话,则通不过CSRF验证,报403错误。做这个实例的时候,不要在setting里面注释这句
'django.middleware.csrf.CsrfViewMiddleware'
带上随机字符串的话, 才能登录成功。从cookie里面先把随机字符串获取到。获取方法如下:$.cookie('csrftoken')
但是后台需要通过key去获取这个值,那么这个值对应的key是什么呢?通过打印settings.CSRF_HEADER_NAME可知,
key是HTTP_X_CSRFTOKEN, HTTP_ 是django自动给加上的,所以我们发送的时候,只需要把key设置成 X_CSRFTOKEN就可以了。
由于请求头里面不能出现下划线,所以最终设置的时候应该写成 X-CSRFTOKEN。
(1) 下面的图说明key是啥样的
(2)下面这个例子来验证HTTP_是Django自动添加的。
(3)理论上我们把请求头设置成X_CSRFTOKEN 就可以了。 但是由于Django有要求,请求头里面不能出现下划线,所以最终我们把请求头设置成X-CSRFTOKEN 的样子。按照官网推荐,建议写成
X-CSRFtoken。
headers:{'X-CSRFtoken':$.cookie('csrftoken')},
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="POST" > {%csrf_token%} <input type="text" name="user" placeholder="user"/> <input type="text" name="pwd" placeholder="pwd"/> <input type="checkbox" name="remember" value="1"/> 10秒免登录 <input type="submit" value="提交"/> <input id="btn" type="button" value="按钮"/> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function(){ $('#btn').click(function(){ $.ajax({ url:'/login/', type:"POST", data:{'user':'root','pwd':'123'}, headers:{'X-CSRFtoken':$.cookie('csrftoken')}, success:function(arg){ } }) }) }) </script> </body> </html>
此时可以用Ajax提交可以成功
1.3 Form表单提交与Ajax方法提交的不同之处在于,去不同的地方拿csrf_token
在 Ajax中写headers太麻烦了,可以用setup 对整个页面中所有的Ajax操作做一个配置。
表示在发送ajax之前,会先执行一下那个函数。
xhr是XMLHttpRequest 对象,所有的ajax操作底层用的都是它。
login.html--------示例中有2个Ajax
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="POST" > {%csrf_token%} <input type="text" name="user" placeholder="user"/> <input type="text" name="pwd" placeholder="pwd"/> <input type="checkbox" name="remember" value="1"/> 10秒免登录 <input type="submit" value="提交"/> <input id="btn1" type="button" value="按钮1"/> <input id="btn2" type="button" value="按钮2"/> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function(){ $.ajaxSetup({ beforeSend:function(xhr,settings){ xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); } }); $('#btn1').click(function(){ $.ajax({ url:'/login/', type:"POST", data:{'user':'root','pwd':'123'}, //headers:{'X-CSRFtoken':$.cookie('csrftoken')}, success:function(arg){ } }) }); $('#btn2').click(function(){ $.ajax({ url:'/login/', type:"POST", data:{'user':'root','pwd':'123'}, //headers:{'X-CSRFtoken':$.cookie('csrftoken')}, success:function(arg){ } }) }) }) </script> </body> </html>
运行效果:两个Ajax都可以成功提交
程序摘录
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="POST" > {%csrf_token%} <input type="text" name="user" placeholder="user"/> <input type="text" name="pwd" placeholder="pwd"/> <input type="checkbox" name="remember" value="1"/> 10秒免登录 <input type="submit" value="提交"/> <input id="btn1" type="button" value="按钮1"/> <input id="btn2" type="button" value="按钮2"/> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function(){ $.ajaxSetup({ beforeSend:function(xhr,settings){ xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); } }); $('#btn1').click(function(){ $.ajax({ url:'/login/', type:"POST", data:{'user':'root','pwd':'123'}, //headers:{'X-CSRFtoken':$.cookie('csrftoken')}, success:function(arg){ } }) }); $('#btn2').click(function(){ $.ajax({ url:'/login/', type:"POST", data:{'user':'root','pwd':'123'}, //headers:{'X-CSRFtoken':$.cookie('csrftoken')}, success:function(arg){ } }) }) }) </script> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>欢迎登录:{{username}},{{request.session.username}}</h1> <a href="/logout/">注销</a> </body> </html>
views.py
from django.shortcuts import render,HttpResponse,redirect # Create your views here. def login(request): if request.method=='GET': return render(request,'login.html') elif request.method=='POST': user=request.POST.get('user') pwd=request.POST.get('pwd') if user=='root' and pwd=='123': #生成随机字符串,写到浏览器cookie中,保存在session中。在随机字符串对应的字典中设置相关内容... # 在session里面设置值 request.session['username']=user request.session['is_login']=True if request.POST.get('remember',None)=='1': #设置超时时间 request.session.set_expiry(10) return redirect('/index/') else: return render(request,'login.html') def index(request): #获取当前用户的随机字符串 #根据随机字符串获取对应的信息 #去session中获取值,如果登录成功,显示用户名 if request.session.get('is_login',None): #return HttpResponse(request.session['username']) return render(request,'index.html',{'username':request.session['username']}) else: return HttpResponse('滚') def logout(request): request.session.clear() return redirect('/login/')
urls.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$', views.login), url(r'^index/$', views.index), url(r'^logout/$', views.logout), ]
二,而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
csrf_exempt
csrf_protect
settings会得到Ajax中的所有数据