DRF提供了BasicAuthentication
、SessionAuthentication
、SessionAuthentication
、TokenAuthentication
、RemoteUserAuthentication
及自定义Authentication
,用于API接口的身份验证。其中,SessionAuthentication默认带有CSRF防御,其他验证方法均未做CSRF防御,本文记录如何在其他验证方法中实现CSRF防御。
一、Authentication及CSRF防御中间件配置
Authentication配置
为了启用Authentication,需要在项目的settings.py
文件中增加REST_FRAMEWORK字段,配置默认验证类DEFAULT_AUTHENTICATION_CLASSES
即可,如下:1
2
3
4
5
6REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
]
}s
上述代码配置当前项目使用SessionAuthentication`
TokenAuthentication进行用户验证,authencation按定义的先后顺序调用
authenticate(self, request)方法进行验证,如果验证成功,返回元祖
(user, auth),分别为
request.user和
request.auth;如果验证失败,返回
None`,继续下一个验证器进行验证,直到所有的验证器都验证完毕;如果其中某个验证器抛出异常,将导致验证器直接停止。
CSRF防御中间件配置
为了启用CSRF防御,需要在项目的settings.py
文件的MIDDLEWARE
字段中增加django.middleware.csrf.CsrfViewMiddleware
,然后配置CSRF_HEADER_NAME
字段,如下:1
2
3
4MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
CSRF_HEADER_NAME = "HTTP_CSRF_TOKEN"
在后续的POST/PUT/DELETE等请求中,通过将cookie中的_csrf_token
与header头中的csrf_token
进行匹配,进行CSRF验证;CSRF验证失败时,将抛出PermissionDenied
异常,返回403状态码,源码如下:1
2
3
4
5
6
7
8
9
10
11def enforce_csrf(self, request):
"""
Enforce CSRF validation for session based authentication.
"""
check = CSRFCheck()
# populates request.META['CSRF_COOKIE'], which is used in process_view()
check.process_request(request)
reason = check.process_view(request, None, (), {})
if reason:
# CSRF failed, bail with explicit error message
raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)
CSRF防御的验证应该放在用户身份验证之后,如果用户不存在,则没有验证CSRF防御的必要。
二、重写TokenAuthentication,实现带CSRF的Token验证
在app目录下创建文件authentication.py
开发CsrfTokenAuthentication
类,继承TokenAuthentication
类,重写authenticate
方法,在用户验证之后验证CSRFToken是否存在,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author:owefsad
# datetime:2020/12/22 上午12:40
# software: PyCharm
# project: vulscan-webapi
from rest_framework import exceptions
from rest_framework.authentication import TokenAuthentication, CSRFCheck
class CsrfTokenAuthentication(TokenAuthentication):
def authenticate(self, request):
user_auth_tuple = super().authenticate(request)
if user_auth_tuple is not None:
self.enforce_csrf(request)
return user_auth_tuple
def enforce_csrf(self, request):
"""
Enforce CSRF validation for session based authentication.
"""
check = CSRFCheck()
# populates request.META['CSRF_COOKIE'], which is used in process_view()
check.process_request(request)
reason = check.process_view(request, None, (), {})
if reason:
# CSRF failed, bail with explicit error message
raise exceptions.AuthenticationFailed('CSRF Failed: %s' % reason)
配置settings.py文件,使用自定义验证器CsrfTokenAuthentication
代替原有的TokenAuthentication
,如下:1
2
3
4
5
6REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'<app>.authentication.CsrfTokenAuthentication',
]
}
启动Django应用即可加载自定义的验证器,实现Token验证防CSRF防御的功能。