Django 安全开发小记
字数 1283 2025-08-07 08:22:23
Django 安全开发最佳实践
1. SECRET_KEY 安全管理
Django 的 SECRET_KEY 在 settings.py 中非常重要,它本质上是一个加密盐,用于:
- 所有会话管理(包括
django.contrib.sessions.backends.cache) get_session_auth_hash()的默认设置- 所有加密签名(除非提供了不同的密钥)
最佳实践:
from django.core.management import utils
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = utils.get_random_secret_key()
建议在生产环境中使用 get_random_secret_key() 自动生成随机密钥。注意在开发环境中,每次修改代码后重新编译会导致密钥变化。
2. 前后端分离身份认证
2.1 JWT 的安全隐患
传统 JWT 认证在密钥泄露时存在伪造用户 Token 的风险。
2.2 Django 原生认证体系
Django 原生认证使用 COOKIE 和 SESSION 机制:
- 通过
django.contrib.auth.login登录 SessionMiddleware的process_response通过 COOKIE 存储 SESSION IDSessionMiddleware的process_request从 COOKIE 获取 SESSION ID
2.3 适配前后端分离的认证方案
2.3.1 新流程
- 登录成功后返回 SESSION ID 给前端
- 前端存储 SESSION ID
- 每次请求在请求头中携带 SESSION ID
2.3.2 登录实现
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login
# 使用 AuthenticationForm 验证后
login(request, user)
# 返回 SESSION ID 给前端
2.3.3 自定义 SessionMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
class RestFulSessionMiddleware(SessionMiddleware):
"""前后端分离重写 Django 默认身份验证"""
def process_request(self, request):
# 如果 COOKIE 是空的就从头部中请求
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
if session_key is None:
# 如请求头中传入的 KEY 为:X-Token 实际上会被转成 HTTP_X_TOKEN
session_key = request.META.get("HTTP_X_TOKEN")
request.session = self.SessionStore(session_key)
2.3.4 禁用 CSRF 认证
from django.utils.deprecation import MiddlewareMixin
class RestFulCsrfViewMiddleware(MiddlewareMixin):
"""API 不设 CSRF 校验"""
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
if session_key is None:
setattr(request, '_dont_enforce_csrf_checks', True)
2.3.5 跨域头处理
class CorsHeadersMiddleware(MiddlewareMixin):
def process_response(self, request, response):
"""处理跨域响应头"""
response['Access-Control-Allow-Methods'] = 'POST,GET,OPTIONS'
response['Access-Control-Max-Age'] = 'POST,GET,OPTIONS'
response['Access-Control-Allow-Headers'] = 'content-type,x-token'
response['Access-Control-Allow-Origin'] = '*'
return response
2.3.6 中间件配置
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',
'common.middleware.CorsHeadersMiddleware', # 新增
'common.middleware.RestFulSessionMiddleware', # 新增
'common.middleware.RestFulCsrfViewMiddleware' # 新增
]
3. Admin Action 后台动作安全
3.1 权限控制演进
旧版 Django 在动作函数中直接检查权限,新版使用 @action 装饰器的 permissions 参数:
from django.contrib import admin
@admin.action(permissions=['change'])
def custom_action(modeladmin, request, queryset):
pass
3.2 常见问题
许多教程未正确设置动作权限,存在安全隐患。
4. Admin Model 复杂数据验证
4.1 重写 save 方法的问题
- 无法获取 request/response 对象
- 异常处理不友好(DEBUG=False 时不显示错误)
4.2 重写 clean 方法
from django.core.exceptions import ValidationError
error_messages = {
'test': '测试验证'
}
def clean(self):
raise ValidationError(self.error_messages['test'], code='test')
4.3 完整字段验证方案
4.3.1 验证流程分析
Model.full_clean调用Model.clean_fieldsclean_fields对每个字段:- 检查空值和
blank=True - 调用
Field.clean Field.clean调用to_python和validate- 执行
validators中的验证器
- 检查空值和
4.3.2 实现方案
from django.db import models
from django.core.exceptions import ValidationError
class BaseModels(models.Model):
"""数据库公共父类"""
class Meta:
abstract = True
def pre_field_clean(self, field: models.Field, raw_value):
"""在 clean_fields() 中,pre_clean_[field.name] 与 models.Field.clean 前调用"""
return raw_value
def post_field_clean(self, field: models.Field, value):
"""在 clean_fields() 中,models.Field.clean 与 post_clean_[field.name] 后调用"""
return value
def clean_fields(self, exclude=None):
"""
重写 clean_fields 方法,为 models 新增自定义字段验证的方法
"""
if exclude is None:
exclude = []
errors = {}
for f in self._meta.fields:
if f.name in exclude:
continue
raw_value = getattr(self, f.attname)
try:
raw_value = self.pre_field_clean(f, raw_value)
if hasattr(self, 'pre_clean_%s' % f.name):
raw_value = getattr(self, 'pre_clean_%s' % f.name)(f, raw_value)
if f.blank and raw_value in f.empty_values and f.name:
continue
value = f.clean(raw_value, self)
if hasattr(self, 'post_clean_%s' % f.name):
value = getattr(self, 'post_clean_%s' % f.name)(f, value)
value = self.post_field_clean(f, value)
setattr(self, f.attname, value)
except ValidationError as e:
errors[f.name] = e.error_list
if errors:
raise ValidationError(errors)
4.3.3 使用示例
class TestValidateModel(BaseModels):
error_messages = {
'null': '名称不能为空'
}
name = models.CharField(max_length=120, null=True, blank=True, verbose_name="名称")
def pre_clean_name(self, field: models.Field, raw_value):
"""自定义验证 name 名称"""
if raw_value in models.Field.empty_values or raw_value is None:
raise ValidationError(self.error_messages['name'], code='name')
return raw_value
总结
本文介绍了 Django 安全开发的四个关键方面:
- SECRET_KEY 的安全管理
- 前后端分离场景下的认证方案
- Admin 后台动作的权限控制
- 复杂数据验证的实现方案
通过遵循这些最佳实践,可以显著提高 Django 应用的安全性。