Django中与CSRF相关的内容
1.什么是CSRF?
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
2.Django下的CSRF认证机制
django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 csrftoken值,把这个 csrftoken 放在 cookie 里。然后每次请求都带着这个值过来完成校验。
token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。官方文档中说到,检验token时,只比较secret是否和cookie中的secret值一样,
而不是比较整个token。django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法,这样就能避免被 CSRF 攻击。
例如:在html 中, 为每个post请求的form 表单增加一个 {% csrf_token %} 标签,它的功能其实是给form表单增加一个隐藏的input标签。服务端收到请求后,
django 会对这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值进行处理,比较secret是否一样,secret一样则本次请求合法。
3.具体的实现方法
django通过中间件 django.middleware.csrf.CsrfViewMiddleware 来防止跨站请求伪造。
#settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',#csrf的认证
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
3-1 form表单当中附加csrftoken
#为每个post请求的form 表单增加一个 {% csrf_token %} 标签,如果不添加,post请求的时候你会发现一个 Forbidden的错误
3-2 Ajax的post请求设置csrf_token的方式
方式1:
通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。
data:{
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), #form表单里必须写{% csrf_token %}#}
},
方式2:
data:{
csrfmiddlewaretoken:"{{ csrf_token }}", #form表单里不用写{% csrf_token %} 模板语法替换产生
},
#示例:
#基于jQuery的实现
方式3:通过获取返回的cookie中的字符串,放置在请求头中发送。
注意:需要引入一个jquery.cookie.js插件,jquery操作cookie。下载地址 https://plugins.jquery.com/cookie/
jquery.cookie.js基于jquery;先引入jquery,再引入:jquery.cookie.js;
$.ajax({
url:'/test/',
type:'post',
headers:{"X-CSRFToken":$.cookie('csrftoken')}, #从Cookie取csrftoken,并设置到请求头headers中
#ajax里面的headers参数,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加
})
注意:
1.如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。
2.如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。-->不是所有请求都有csrftoken这个cookie键值对
3.这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。
django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie #强制给请求对应的响应添加csrftoken这个cookie键值对
def login(request):
pass
3-3 CSRF Token相关装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect
csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置csrfToken全局中间件。
csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
示例1:
#views.py
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# @csrf_exempt
@csrf_protect
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'zhangsan' and password == '1234':
return HttpResponse('OK')
else:
return redirect('login')
#login.html
#settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
【1】settings中设置了全局中间件,在login.html中不设置{% csrf_token %},当post提交数据时,Forbidden (403)
如果在FBV的函数上使用csrf_exempt装饰器,可以成功提交数据。
【2】settings中没有设置全局中间件,当post提交数据时可以成功提交。如果在FBV的函数上使用csrf_exempt装饰器,Forbidden (403)。
示例2:注意csrf-token装饰器的特殊性,在CBV模式下它只能加在dispatch上面。
添加装饰器的格式必须为@method_decorator(),括号里面为装饰器的函数名
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
#@method_decorator(csrf_exempt, name='dispatch')
class HomeView(View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, "home.html")
def post(self, request):
print("Home Viw POST method...")
return redirect("/index/")