Django-CSRF,Ajax的两种验证方法
CSRF:
CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
请求伪造是一种挟制终端用户在当前已登录的Web应用程序上执行非本意的操作。
例:
CSRF攻击的主要目的是让用户在不知情的情况下攻击自己已登录的一个系统,类似于钓鱼。如用户当前已经登录了邮箱,或bbs,同时用户又在使用另外一个,已经被你控制的站点,我们姑且叫它钓鱼网站。这个网站上面可能因为某个图片吸引你,你去点击一下,此时可能就会触发一个js的点击事件,构造一个bbs发帖的请求,去往你的bbs发帖,由于当前你的浏览器状态已经是登陆状态,所以session登陆cookie信息都会跟正常的请求一样,纯天然的利用当前的登陆状态,让用户在不知情的情况下,帮你发帖或干其他事情。
当CSRF针对普通用户发动攻击时,将对终端用户的数据和操作指令构成严重的威胁;当受攻击的终端用户具有管理员帐户的时候,CSRF攻击将危及整个Web应用程序。
原理:
以img的自动加载图片为例
假如说,攻击者通过给网站,使用户点击网站链接进行跳转,从而跳转到了一个新的网站,而这个网站中的HTML 代码是这样的
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> </head> <body> <img src="https://www.xxxx.xxxx/xxxx" height="0" width="0"> </body> </html>
很明显的这张图片是不会显示出来,但是在访问网站的时候,所有的图片都是自动加载(除非禁用),从而自动的向img中所包含url发送请求。
假如,此时,用户正在登陆别的网站,这时候,就会发生CSRF攻击,用户的关键信息就会被盗取,从而造成损失。
防范:
当访问网站的时候都是进行GET请求,在服务器给用户返回form表单中添加一个随机字符串,
而在form表单提交POST请求时,会将这个字符串返回给服务器进行验证。
Django中为了防止CSRF攻击,给我们提供了方法:
在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',
]
views.py
def csrf(request): if request.method=='GET': return render(request,'image.html') else: return HttpResponse('提交成功')
csrf.py:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> </head> <body> <form action="csrf.html" method="post" > <input type="text" name=""> <input type="submit" value="提交"> </form> </body> </html>
注意:
settings.py 模块中:'django.middleware.csrf.CsrfViewMiddleware',没被注释的时候,表示后台会开启表单验证,如果有form表单中提交时没有随机验证码的话,会禁止提交:
我们只需要在返回的form表单中添加响应的token就可以通过验证进行提交:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> </head> <body> <form action="csrf.html" method="post" > {% csrf_token %} <input type="text" name=""> <input type="submit" value="提交"> </form> </body> </html>
让我们在网页查看源代码中看一下:
那如果说想要网站的一部分form表单提交不做验证的话,只需要添加一个特殊的装饰器就可以(全局开启验证情况下)
views.py:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt # 表示对csrf内所提交的form表单不做验证,局部禁用
def csrf(request):
if request.method=='GET':
return render(request,'image.html')
else:
return HttpResponse('提交成功')
全局禁用验证,局部开启:
views.py:
from django.views.decorators.csrf import csrf_protect
@csrf_protect #全局禁用验证条件下,局部开启form表单验证
def csrf(request):
if request.method=='GET':
return render(request,'image.html')
else:
return HttpResponse('提交成功')
CBV中添加 csrf_protect/csrf_exempt 装饰器
在CBV中(类/方法)添加基本的装饰器,是需要用到特殊的方法来进行装饰:
from django.views import View from django.views.decorators.csrf import csrf_protect,csrf_exempt from django.utils.decorators import method_decorator def wrapper(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner @method_decorator(wrapper,name='get') # 给类中的get方法添加装饰器 class Login(View): @method_decorator(wrapper) #给类中函数添加装饰器 def dispatch(self, request, *args, **kwargs): obj = super(Login,self).dispatch(request, *args, **kwargs) return obj def get(self,request): username = request.GET.get('name'); return HttpResponse('GET') def post(self,request): username = request.POST.get('name') return HttpResponse('Post')
但是,Django中,如果给CBV中添加csrf_exempt/csrf_protect装饰器的时候,是不可以直接添加到类中的函数上,只能从类上添加:
from django.views import View from django.views.decorators.csrf import csrf_protect,csrf_exempt from django.utils.decorators import method_decorator @method_decorator(csrf_protect,'dispath') class Login(View): def dispatch(self, request, *args, **kwargs): obj = super(Login,self).dispatch(request, *args, **kwargs) return obj def get(self,request): username = request.GET.get('name'); return HttpResponse('GET') def post(self,request): username = request.POST.get('name') return HttpResponse('Post')
Ajax请求的两种方式
1、将验证的值csrfmiddlewaretoken放置在data中携带
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX-CSRF</title> </head> <body> <form action=""> {% csrf_token %} <input id='user'type="text" name="user"> <input id=userForm onclick="button_Form()" type="button" value="提交"> </form> <script src="/static/jquery-3.2.1.js"></script> <script> function button_Form() { var csrf_val = $('input[name="csrfmiddlewaretoken"]').val(); var user_val = $('#user').val(); $.ajax({ url:'{% url "ajax-csrf" %}', type:'POST', data:{ 'user':user_val, 'csrfmiddlewaretoken':csrf_val, }, success:function(arg) { console.log(arg) } }) } </script> </body> </html>
2、通过JS在cookie中获取,放在请求头中携带。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX-CSRF-2</title> </head> <body> <form action=""> <input id='user'type="text" name="user"> <input id=userForm onclick="button_Form()" type="button" value="提交"> </form> <script src="/static/jquery-3.2.1.js"></script> <script src="/static/jquery.cookie.js"></script> 可以通过jquery.cookie来进行获取cookie。 <script> function button_Form() { var user_val = $('#user').val(); var csrf_val = $.cookie('csrftoken'); alert(csrf_val); $.ajax({ url:'{% url "ajax-csrf" %}', type:'POST', headers:{'X-CSRFToken':csrf_val}, 在headers中添加X-CSRFToken的随机验证 data:{ 'user':user_val, }, success:function(arg) { console.log(arg) } }) } </script> </body> </html>