本文介绍在 django 中安全存储用户级敏感数据(如第三方 api 密钥)的最佳实践,重点推荐使用 django-fernet-encrypted-fields 对数据库字段进行应用层透明加密,并说明其使用限制与关键注意事项。
本文介绍在
django
中安全存储用户级
敏感数据
(如第三方 api 密钥)的最佳实践,重点推荐使用 django-fernet-encrypted-fields 对数据库字段进行应用层透明加密,并说明其使用限制与关键注意事项。
在 Django 应用中,为每位用户关联并安全存储第三方服务的 API 密钥,是一项常见但高风险的需求。直接明文存储于数据库严重违背最小权限与数据保密原则;而将密钥交由客户端或环境变量管理又无法实现“按用户隔离”。
最合理且被广泛验证的方案是:在 Django 应用层对敏感字段执行对称加密(如 Fernet),再将密文持久化至数据库。
推荐使用由 Jazzband 维护的成熟第三方库
django-fernet-encrypted-fields
。它基于 Python 标准库 cryptography 实现 AES-128-CBC 加密,自动处理密钥派生、随机 IV 生成与认证标签(AEAD),确保机密性与完整性。
✅ 快速集成示例
首先安装:
在 settings.py 中配置主密钥(
必须严格保密,禁止提交至版本控制
):
Python 网站开发之Django环境搭建 WORD版
本文档主要讲述的是Python 网站开发之Django环境搭建;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
定义模型(关联到 User 或自定义用户扩展模型):
使用时如同普通字段:
⚠️ 关键限制与注意事项(务必遵守)
查询能力受限
:加密在 Django 应用层完成,数据库仅存储密文。因此
无法对 EncryptedTextField 执行 filter()、exclude()、__icontains 等查询操作
——所有匹配必须先加载对象再在内存中比对(极低效,不推荐)。
禁止添加数据库约束
:该字段
不可设为 primary_key、unique=True、db_index=True
,也不应参与 ORDER BY 或 GROUP BY。违反将导致数据不一致或查询失败。
密钥安全管理
:ENCRYPTION_KEY 是解密唯一凭证。生产环境必须通过安全方式注入(如 Kubernetes Secret、AWS SSM Parameter Store),
绝不可硬编码或写入 Git
。
备份与迁移
:数据库备份包含密文,但恢复时必须确保 ENCRYPTION_KEY 完全一致,否则所有密钥永久丢失。
替代方案权衡
:若需高频模糊搜索或索引查询,应考虑将非敏感标识符(如哈希后 token ID)存于数据库,密钥本身托管至专用密钥管理服务(如 HashiCorp Vault、AWS KMS),通过短期令牌动态获取——但这会增加架构复杂度。
综上,EncryptedTextField 是 Django 生态中平衡安全性、易用性与合规性的首选方案。只要明确其设计边界(即“仅支持精确读取,不支持数据库侧查询”),即可稳健支撑用户级密钥管理场景。
pip install django-fernet-encrypted-fields# settings.py
import os
from cryptography.fernet import Fernet
# 推荐:从环境变量读取(如 Docker secrets 或 .env)
ENCRYPTION_KEY = os.environ.get('DJANGO_ENCRYPTION_KEY')
if not ENCRYPTION_KEY:
# 开发时可生成临时密钥(生产环境务必替换!)
ENCRYPTION_KEY = Fernet.generate_key().decode()# models.py
from django.db import models
from django.contrib.auth.models import User
from encrypted_fields.fields import EncryptedTextField
class UserAPIKey(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='api_key')
key = EncryptedTextField() # 自动加密/解密,对业务代码完全透明
def __str__(self):
return f"API key for {self.user.username}"# views.py 或业务逻辑中
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404
@login_required
def fetch_external_data(request):
api_key_obj = get_object_or_404(UserAPIKey, user=request.user)
raw_api_key = api_key_obj.key # 自动解密,返回 str 类型明文
# → 调用第三方服务...