用于处理pgcrypto postgres扩展的Django加密字段。
项目描述
django-pgcrypto-fields
django-pgcrypto-fields
是一个依赖于pgcrypto
的Django
扩展,用于加密和解密字段的 数据。
需求
- 带有
pgcrypto
的postgres - 支持Django 2.2.x, 3.0.x, 3.1.x和3.2.x
- 仅兼容Python 3
此库支持Django
1.8.x, 1.9.x, 1.10.x的最后一个版本是django-pgcrypto-fields
2.2.0。
此库支持Django
2.0.x和2.1.x的最后一个版本是django-pgcrypto-fields
2.5.2。
安装
安装包
pip install django-pgcrypto-fields
Django设置
我们的库通过在DATABASES
设置中定义密钥来支持多个数据库的不同加密密钥。
在settings.py
import os
BASEDIR = os.path.dirname(os.path.dirname(__file__))
PUBLIC_PGP_KEY_PATH = os.path.abspath(os.path.join(BASEDIR, 'public.key'))
PRIVATE_PGP_KEY_PATH = os.path.abspath(os.path.join(BASEDIR, 'private.key'))
# Used by PGPPublicKeyField used by default if not specified by the db
PUBLIC_PGP_KEY = open(PUBLIC_PGP_KEY_PATH).read()
PRIVATE_PGP_KEY = open(PRIVATE_PGP_KEY_PATH).read()
# Used by TextHMACField and PGPSymmetricKeyField if not specified by the db
PGCRYPTO_KEY='ultrasecret'
DIFF_PUBLIC_PGP_KEY_PATH = os.path.abspath(
os.path.join(BASEDIR, 'tests/keys/public_diff.key')
)
DIFF_PRIVATE_PGP_KEY_PATH = os.path.abspath(
os.path.join(BASEDIR, 'tests/keys/private_diff.key')
)
# And add 'pgcrypto' to `INSTALLED_APPS` to create the extension for
# pgcrypto (in a migration).
INSTALLED_APPS = (
'pgcrypto',
# Other installed apps
)
DATABASES = {
# This db will use the default keys above
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'pgcryto_fields',
'USER': 'pgcryto_fields',
'PASSWORD': 'xxxx',
'HOST': 'psql.test.com',
'PORT': 5432,
'OPTIONS': {
'sslmode': 'require',
}
},
'diff_keys': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'pgcryto_fields_diff',
'USER': 'pgcryto_fields_diff',
'PASSWORD': 'xxxx',
'HOST': 'psqldiff.test.com',
'PORT': 5432,
'OPTIONS': {
'sslmode': 'require',
},
'PGCRYPTO_KEY': 'djangorocks',
'PUBLIC_PGP_KEY': open(DIFF_PUBLIC_PGP_KEY_PATH, 'r').read(),
'PRIVATE_PGP_KEY': open(DIFF_PRIVATE_PGP_KEY_PATH, 'r').read(),
},
}
如果使用公钥加密,则生成GPG密钥
公钥将用于加密消息,而私钥将用于解密内容。以下命令取自pgcrypto 文档(参见使用GnuPG生成PGP密钥)。
生成一个公钥和一个私钥(推荐的关键类型是“DSA和Elgamal”。)
$ gpg --gen-key
$ gpg --list-secret-keys
/home/bob/.gnupg/secring.gpg
---------------------------
sec 2048R/21 2014-10-23
uid Test Key <example@example.com>
ssb 2048R/42 2014-10-23
$ gpg -a --export 42 > public.key
$ gpg -a --export-secret-keys 21 > private.key
限制
此库目前不支持密码保护的公钥加密私钥。有关实现它的信息,请参阅问题#89。
从先前版本升级到2.4.0
此库的2.4.0版本进行了大量重写,以支持在获取加密字段数据时自动解密,以及在不使用先前版本中可用的旧PGPCrypto聚合函数的情况下过滤加密字段的能力。
以下库中的项目已被移除,因此您需要从应用程序中删除对这些项目的引用
managers.PGPManager
admin.PGPAdmin
aggregates.*
字段
django-pgcrypto-fields
有 3 种字段类型
- 基于哈希的字段
- 公钥(PGP)字段
- 对称字段
基于哈希的字段
支持的基于哈希的字段有
TextDigestField
TextHMACField
TextDigestField
使用 digest
pgcrypto 函数和 sha512
算法在数据库中哈希。
TextHMACField
使用 hmac
pgcrypto 函数和一个密钥以及 sha512
算法在数据库中哈希。这与摘要版本类似,但只能知道密钥才能重新计算哈希。这可以防止有人更改数据并更改哈希以匹配。
公钥加密字段
支持的PGP公钥字段有
CharPGPPublicKeyField
EmailPGPPublicKeyField
TextPGPPublicKeyField
DatePGPPublicKeyField
DateTimePGPPublicKeyField
TimePGPPublicKeyField
IntegerPGPPublicKeyField
BigIntegerPGPPublicKeyField
DecimalPGPPublicKeyField
FloatPGPPublicKeyField
公钥加密使用公钥生成令牌以加密数据,并使用私钥进行解密。
公钥和私钥可以使用 PUBLIC_PGP_KEY
和 PRIVATE_PGP_KEY
在设置中设置。
对称密钥加密字段
支持的PGP对称密钥字段有
CharPGPSymmetricKeyField
EmailPGPSymmetricKeyField
TextPGPSymmetricKeyField
DatePGPSymmetricKeyField
DateTimePGPSymmetricKeyField
TimePGPSymmetricKeyField
IntegerPGPSymmetricKeyField
BigIntegerPGPSymerticKeyField
DecimalPGPSymmetricKeyField
FloatPGPSymmetricKeyField
使用 settings.PGCRYPTO_KEY
加密和解密数据,它充当密码。
Django 模型字段等效
Django 字段 | 公钥字段 | 对称密钥字段 |
---|---|---|
CharField |
CharPGPPublicKeyField |
CharPGPSymmetricKeyField |
EmailField |
EmailPGPPublicKeyField |
EmailPGPSymmetricKeyField |
TextField |
TextPGPPublicKeyField |
TextPGPSymmetricKeyField |
DateField |
DatePGPPublicKeyField |
DatePGPSymmetricKeyField |
DateTimeField |
DateTimePGPPublicKeyField |
DateTimePGPSymmetricKeyField |
TimeField |
TimePGPPublicKeyField |
TimePGPSymmetricKeyField |
IntegerField |
IntegerPGPPublicKeyField |
IntegerPGPSymmetricKeyField |
BigIntegerField |
BigIntegerPGPPublicKeyField |
BigIntegerPGPSymmetricKeyField |
DecimalField |
DecimalPGPPublicKeyField |
DecimalPGPSymmetricKeyField |
FloatField |
FloatPGPPublicKeyField |
FloatPGPSymmetricKeyField |
其他Django模型字段目前不受支持。欢迎提交拉取请求。
用法
模型定义
from django.db import models
from pgcrypto import fields
class MyModel(models.Model):
digest_field = fields.TextDigestField()
digest_with_original_field = fields.TextDigestField(original='pgp_sym_field')
hmac_field = fields.TextHMACField()
hmac_with_original_field = fields.TextHMACField(original='pgp_sym_field')
email_pgp_pub_field = fields.EmailPGPPublicKeyField()
integer_pgp_pub_field = fields.IntegerPGPPublicKeyField()
pgp_pub_field = fields.TextPGPPublicKeyField()
date_pgp_pub_field = fields.DatePGPPublicKeyField()
datetime_pgp_pub_field = fields.DateTimePGPPublicKeyField()
time_pgp_pub_field = fields.TimePGPPublicKeyField()
decimal_pgp_pub_field = fields.DecimalPGPPublicKeyField()
float_pgp_pub_field = fields.FloatPGPPublicKeyField()
email_pgp_sym_field = fields.EmailPGPSymmetricKeyField()
integer_pgp_sym_field = fields.IntegerPGPSymmetricKeyField()
pgp_sym_field = fields.TextPGPSymmetricKeyField()
date_pgp_sym_field = fields.DatePGPSymmetricKeyField()
datetime_pgp_sym_field = fields.DateTimePGPSymmetricKeyField()
time_pgp_sym_field = fields.TimePGPSymmetricKeyField()
decimal_pgp_sym_field = fields.DecimalPGPSymmetricKeyField()
float_pgp_sym_field = fields.FloatPGPSymmetricKeyField()
加密
数据在插入数据库时会自动加密。
示例
>>> MyModel.objects.create(value='Value to be encrypted...')
如果使用 original
属性,基于哈希的字段可以自动更新哈希。此属性允许您指定其他字段名称,以便基于该字段名称的哈希值。
from django.db import models
from pgcrypto import fields
class User(models.Model):
first_name = fields.TextPGPSymmetricKeyField(max_length=20, verbose_name='First Name')
first_name_hashed = fields.TextHMACField(original='first_name')
在上面的示例中,如果指定可选的 original
属性,则将使用第一个_name 模型字段的无加密值作为输入值来创建哈希。如果没有指定 original
属性,则字段将像现在一样工作,并保持向后兼容。
PGP 字段
当访问模型实例的字段名称属性时,我们得到解密值。
示例
>>> # When using a PGP public key based encryption
>>> my_model = MyModel.objects.get()
>>> my_model.value
'Value decrypted'
从 2.4.0 版本开始,自动解密处理加密值过滤,并且不再支持 aggregate
方法,并已从库中删除。
此外,自动解密也支持 select_related
模型。
from django.db import models
from pgcrypto import fields
class EncryptedFKModel(models.Model):
fk_pgp_sym_field = fields.TextPGPSymmetricKeyField(blank=True, null=True)
class EncryptedModel(models.Model):
pgp_sym_field = fields.TextPGPSymmetricKeyField(blank=True, null=True)
fk_model = models.ForeignKey(
EncryptedFKModel, blank=True, null=True, on_delete=models.CASCADE
)
示例
>>> import EncryptedModel
>>> my_model = EncryptedModel.objects.get().select_releated('fk_model')
>>> my_model.pgp_sym_field
'Value decrypted'
>>> my_model.fk_model.fk_pgp_sym_field
'Value decrypted'
基于哈希的字段
为了过滤基于哈希的值,我们需要比较哈希。这是通过使用 __hash_of
查找来实现的。
示例
>>> my_model = MyModel.objects.filter(digest_field__hash_of='value')
[<MyModel: MyModel object>]
>>> my_model = MyModel.objects.filter(hmac_field__hash_of='value')
[<MyModel: MyModel object>]
限制
.distinct('encrypted_field_name')
由于Django ORM中缺少一个功能,在Django 2.0.x及以下版本中,对加密字段使用distinct()
不工作。
在Django 2.1.x及更高版本中,正常的distinct功能可以工作。
items = EncryptedFKModel.objects.filter(
pgp_sym_field__startswith='P'
).only(
'id', 'pgp_sym_field', 'fk_model__fk_pgp_sym_field'
).distinct(
'pgp_sym_field'
)
Django 2.0.x及以下版本的解决方案
from django.db import models
items = EncryptedFKModel.objects.filter(
pgp_sym_field__startswith='P'
).annotate(
_distinct=models.F('pgp_sym_field')
).only(
'id', 'pgp_sym_field', 'fk_model__fk_pgp_sym_field'
).distinct(
'_distinct'
)
这是因为注释的字段被Django自动解密为F
字段,并在distinct()
中使用该字段。
将现有字段迁移到PGCrypto字段
此库不执行将现有字段迁移到PGCrypto字段的操作。您需要通过前向迁移或其他方式进行数据迁移。除在Postgres中创建/激活pgcrypto扩展之外,还支持迁移。
迁移数据很复杂,可能需要考虑以下因素
- 数据形状
- 在表/模型/表单以及任何其他地方执行的验证/约束
库无法做所有这些猜测或做出所有这些决定。
如果您需要将数据从未加密字段迁移到加密字段,有三种解决方法
- 当数据库中没有数据时,应该可以从从头开始重建数据库。
- 当表中没有数据时,应该可以重建表。
- 当有数据或项目是共享的时,应该可以以非破坏性的方式进行迁移。
选项1:数据库中没有数据
- 删除数据库
- 压缩迁移
- 重新创建数据库
选项2:表中没有数据
- 创建一个迁移以删除表
- 为具有加密字段的表创建一个新的迁移
- 可选地压缩迁移
选项3:以非破坏性的方式进行迁移
这里的目的是在出现问题的情况下能够使用旧字段。
第一部分
- 创建新字段
- 当数据保存时,将数据写入旧字段和新字段
- 创建一个数据迁移来将数据从旧字段转换为新字段
- 如果可能,检查旧字段和新字段中的现有数据是否相同
第二部分
- 重命名字段并删除旧字段
- 更新代码以只使用新字段
安全限制
参考PostgreSQL文档中的说明
https://postgresql.ac.cn/docs/9.6/static/pgcrypto.html#AEN187024
所有pgcrypto函数都在数据库服务器内部运行。这意味着所有数据和密码都在pgcrypto和客户端应用程序之间以明文形式传输。因此,您必须
- 本地连接或使用SSL连接。
- 信任系统和数据库管理员。
如果您无法这样做,那么在客户端应用程序内部进行加密会更好。
该实现无法抵抗侧信道攻击。例如,给定大小的密文对pgcrypto解密函数完成所需的时间可能会有所不同。
变更日志
主分支(未发布)
2.6.0
- 添加了对Django 3.1.x的支持
- 更新了requirements_dev.txt
- 删除了对Python 3.5的支持
- 删除了对Django 2.2.x LTS以下版本的支持
- 添加了对BigIntegerFields的支持 (#169)
- 添加了现有数据迁移的文档 (#246)
2.5.2
- 添加了对Django 3.x的支持
- 更新了requirements_dev.txt
2.5.1
- 修复了EmailPGPPublicKeyField定义中的回归 (#77)
- 删除了死代码(remove_validators和RemoveMaxLengthValidatorMixin)
- 更新了requirements_dev.txt
2.5.0
- 为公钥和对称密钥添加了新的DecimalFields (#64)
- 为公钥和对称密钥添加了新的FloatFields (#64)
- 为公钥和对称密钥添加了新的TimeFields (#64)
- 根据数据库添加了对不同密钥的支持 (#67)
2.4.0
- 自动解密所有加密字段,包括FK表
- 删除了django-pgcrypto-fields的
aggregates
、PGPManager
和PGPAdmin
,因为它们不再需要 - 添加了对
get_or_create()
和update_or_create()
的支持 (#27) - 添加了对
get_by_natural_key()
的支持 (#23) - 添加了对
only()
和defer()
的支持,因为它们在PGPManager
中不受支持 - 添加了对
distinct()
的支持(Django 2.1+,并为 2.0 及以下版本提供了解决方案) - 将开发要求从 setup.py 要求中分离出来
- 更新了打包/设置.py,包括长描述
- 添加了作者信息和更新了贡献指南
- 更新了 TravisCI 以使用 Xenial,以在矩阵中获得 Python 3.7
2.3.1
- 为日期/日期时间字段添加了
__range
查询(#59) - 移除对
Django 1.8, 1.9 和 1.10
的兼容性(#62) - 改进了
setup.py
- 检查 Python 3.5+
- 更新了分类器
- 改进了用于发布的
make
文件,以使用twine
- 向
README
添加了额外的徽章 - 更新 Travis 配置以包括 Python 3.5 和 3.6
- 重构了查询和混合
2.3.0
- 无效的发布,升级到 2.3.1
2.2.0
- 将
.coveragerc
合并到setup.cfg
- 添加了
.gitignore
文件 - 更新了过时的要求(最新版本的
Flake8
和pycodestyle
互不兼容) - 更新了
README
,以更好地解释字段 - 实现了 DatePGPPublicKeyField 和 DateTimePGPPublicKeyField
2.1.1
- 添加了对 Django 2.x+ 的支持
- 更新了测试要求
- 更新 Travis 配置以包括 Python 3.6 和其他环境
2.1.0
感谢 @peterfarrell
- 添加了对
DatePGPSymmetricKeyField
和DateTimePGPSymmetricKeyField
的支持,包括对序列化和反序列化 django 表单字段的兼容性。 - 通过 PGPManager 添加了对对称密钥和公钥字段的自动解密支持(并通过 PGPAdmin 在 Django Admin 中禁用支持)
2.0.0
- 移除对
Django 1.7
的兼容性。 - 添加对
Django 1.10
的兼容性。 - 将
Django 1.9
添加到 Travis 矩阵中。
v1.0.1
- 从分发包中排除测试应用程序。
v1.0.0
- 将包名从
pgcrypto_fields
更改为pgcrypto
。
v0.7.0
- 使
get_placeholder
接受新的参数compiler
- 修复了
Aggregate
的错误导入
注意:这些更改已针对 django > 1.8.0 执行。
v0.6.4
- 从电子邮件字段中删除
MaxLengthValidator
v0.6.3
- 避免在 PGP 字段上设置
max_length
v0.6.2
- 允许/检查以下字段的
NULL
值:TextDigestField
;TextHMACField
;EmailPGPPublicKeyField
;IntegerPGPPublicKeyField
;TextPGPPublicKeyField
;EmailPGPSymmetricKeyField
。IntegerPGPSymmetricKeyField
。TextPGPSymmetricKeyField
。
v0.6.1
- 修复了向整数字段发送负值时的
cast
错误。
v0.6.0
- 添加了
EmailPGPPublicKeyField
和EmailPGPSymmetricKeyField
。
v0.5.0
- 重命名以下字段:
PGPPublicKeyField
到TextPGPPublicKeyField
;PGPSymmetricKeyField
到TextPGPSymmetricKeyField
;DigestField
到TextDigestField
;HMACField
到TextHMACField
。 - 添加了新的整数字段:
IntegerPGPPublicKeyField
;IntegerPGPSymmetricKeyField
。
v0.4.0
- 使访问解密值透明。修复了当字段具有 PGP 和基于密钥的哈希字段的字符串表示为
memoryview
时的错误。
v0.3.1
- 修复了
EncryptedProxyField
以选择正确的项。
v0.3.0
- 使用字段的代理
_decrypted
访问PGPPublicKeyField
和PGPSymmetricKeySQL
的解密值。 - 删除字段名称和原始值的描述符。
v0.2.0
- 为
DigestField
和HMACField
添加基于哈希的查找。 - 添加
DigestField
、HMACField
、PGPPublicKeyAggregate
、PGPSymmetricKeyAggregate
。
v0.1.0
- 通过聚合类添加了解密。
- 在向数据库插入数据时添加了加密。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。
源分发
构建分发
哈希值 for django_pgcrypto_fields-2.6.0-py3-none-any.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 52bdfc95309e6d281f7c4f3778d4d04da0c87d38b5ac69b4b3832ae9930ab789 |
|
MD5 | b946b5f5954201f49f7b34daace551b0 |
|
BLAKE2b-256 | b3fc6ddceda97131c85c08f3abf7d1628513648550880da3dfe905187cee407f |