Python Django - CSRF跨站请求伪造防护
前言
CSRF 全称 Cross-site request forgery(跨站请求伪造),是一种网络的攻击方式,也被称为“One Click Attack”或者 Session Riding,通常缩写为 CSRF 或者 XSRF。
攻击原理
1、用户访问正常的网站 A,浏览器就会保存网站 A 的 cookies。
2、用户在访问恶意网站 B, 网站 B 上有某个隐藏的链接会自动请求网站 A 的链接地址,例如表单提交,传指定的参数。
3、恶意网站 B 的自动化请求,执行就是在用户 A 的同一个浏览器上,因此在访问网站 A 的时候,浏览器会自动带上网站 A 的 cookies。
4、所以网站 A 在接收到请求之后,可判断当前用户登录状态,所以根据用户的权限做具体的操作逻辑。
防范措施
1、在指定表单或者请求头的里面添加一个随机值作为参数。
2、在响应的 cookie 里面也设置该随机值。
3、用户正常提交表单的时候会默认带上表单中的随机值,浏览器会自动带上 cookie 里面的随机值,那么服务器下次接收到请求之后就可以取出两个值进行校验。
4、对于网站 B 来说网站 B 在提交表单的时候不知道该随机值是什么,所以就形成不了攻击。
Django 中 CSRF 中间件
django 在创建项目的时候,默认就会有添加中间进行 CSRF 的保护,在 MIDDLEWARE 可以看到加载了 django.middleware.csrf.CsrfViewMiddleware 的中间件,这里是全局设置,也可以局部设置。
全局保护:直接启用中间件就可以了。
局部保护:from django.views.decorators.csrf import csrf_exempt,csrf_protect,使用装饰器进行验证。csrf_protect:为当前函数强制设置防跨站请求伪造功能,即便 settings 中没有设置全局中间件;csrf_exempt:取消当前函数防跨站请求伪造功能,即便 settings 中设置了全局中间件。
验证
在 POST 请求提交数据的时候,django 会去检查是否有一个 csrf 的随机字符串,如果没有就会返回 403 没有权限访问。
表单验证
在 form 表单里面需要添加{%csrf_token%},Django 会自动渲染隐藏的 input 输入框:
<input
type="hidden"
name="csrfmiddlewaretoken"
value="2Sb0DQwDVgOQ8i3n1BaG1MUPLEYr6ZGaCLYa14maOQM0Ami5ddQOR6hfXuD2mrmA"
/>
在表单提交的时候,中间件会验证 csrfmiddlewaretoken。
通过 ajax 提交
通过 cookies 获取到 csrftoken,
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
$("#submit").click(function () {
$.ajax({
url:"/login/",
type:"POST",
data:{"usr":"admin","pwd":"HpFledIS3ef"},
headers:{ "X-CSRFtoken":getCookie('csrftoken'),
success:function (arg) {
}
})
})
局部禁用或者启用
1、如果是函数视图,可以直接在函数加上装饰器即可:
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
return HttpResponse('ok')
2、如果是类视图,需要使用方法装饰器进行封装
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.views.generic import TemplateView
@method_decorator(csrf_exempt)
class LoginView(TemplateView):
template_name = 'login.html'
def post():
return HttpResponse('ok')
3、直接装饰 as_view()方式,在 URLconf 里面设置。
from django.views.decorators.csrf import csrf_exempt,csrf_protect
urlpatterns = [
path('login/', csrf_exempt(LoginView.as_view()),name="login"),
]
- 原文作者:Linux运维菜
- 原文链接:https://www.opcai.top/post/2019/2019-01/django_csrf/
- 版权声明:本作品采用进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。