Django 安全开发小记
字数 1283 2025-08-07 08:22:23

Django 安全开发最佳实践

1. SECRET_KEY 安全管理

Django 的 SECRET_KEYsettings.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 机制:

  1. 通过 django.contrib.auth.login 登录
  2. SessionMiddlewareprocess_response 通过 COOKIE 存储 SESSION ID
  3. SessionMiddlewareprocess_request 从 COOKIE 获取 SESSION ID

2.3 适配前后端分离的认证方案

2.3.1 新流程

  1. 登录成功后返回 SESSION ID 给前端
  2. 前端存储 SESSION ID
  3. 每次请求在请求头中携带 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 验证流程分析

  1. Model.full_clean 调用 Model.clean_fields
  2. clean_fields 对每个字段:
    • 检查空值和 blank=True
    • 调用 Field.clean
    • Field.clean 调用 to_pythonvalidate
    • 执行 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 安全开发的四个关键方面:

  1. SECRET_KEY 的安全管理
  2. 前后端分离场景下的认证方案
  3. Admin 后台动作的权限控制
  4. 复杂数据验证的实现方案

通过遵循这些最佳实践,可以显著提高 Django 应用的安全性。

Django 安全开发最佳实践 1. SECRET_ KEY 安全管理 Django 的 SECRET_KEY 在 settings.py 中非常重要,它本质上是一个加密盐,用于: 所有会话管理(包括 django.contrib.sessions.backends.cache ) get_session_auth_hash() 的默认设置 所有加密签名(除非提供了不同的密钥) 最佳实践: 建议在生产环境中使用 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 ID SessionMiddleware 的 process_request 从 COOKIE 获取 SESSION ID 2.3 适配前后端分离的认证方案 2.3.1 新流程 登录成功后返回 SESSION ID 给前端 前端存储 SESSION ID 每次请求在请求头中携带 SESSION ID 2.3.2 登录实现 2.3.3 自定义 SessionMiddleware 2.3.4 禁用 CSRF 认证 2.3.5 跨域头处理 2.3.6 中间件配置 3. Admin Action 后台动作安全 3.1 权限控制演进 旧版 Django 在动作函数中直接检查权限,新版使用 @action 装饰器的 permissions 参数: 3.2 常见问题 许多教程未正确设置动作权限,存在安全隐患。 4. Admin Model 复杂数据验证 4.1 重写 save 方法的问题 无法获取 request/response 对象 异常处理不友好(DEBUG=False 时不显示错误) 4.2 重写 clean 方法 4.3 完整字段验证方案 4.3.1 验证流程分析 Model.full_clean 调用 Model.clean_fields clean_fields 对每个字段: 检查空值和 blank=True 调用 Field.clean Field.clean 调用 to_python 和 validate 执行 validators 中的验证器 4.3.2 实现方案 4.3.3 使用示例 总结 本文介绍了 Django 安全开发的四个关键方面: SECRET_ KEY 的安全管理 前后端分离场景下的认证方案 Admin 后台动作的权限控制 复杂数据验证的实现方案 通过遵循这些最佳实践,可以显著提高 Django 应用的安全性。