gpt4 book ai didi

python - 如何创建一个加密的 django 字段来转换从数据库中检索到的数据?

转载 作者:太空狗 更新时间:2023-10-29 17:17:42 24 4
gpt4 key购买 nike

我有一个自定义的 EncryptedCharField,我想在连接 UI 时基本上显示为 CharField,但在数据库中存储/检索之前,它会对其进行加密/解密。

custom fields documentation说:

  1. 添加 __metaclass__ = models.SubfieldBase
  2. 覆盖 to_python 以将数据从其原始存储转换为所需的格式
  3. 覆盖 get_prep_value 以在存储 ot 数据库之前转换值。

所以您认为这很容易 - 2. 只需解密值,3. 只需对其加密。

松散地基于 a django snippet ,该字段的文档如下所示:

class EncryptedCharField(models.CharField):
"""Just like a char field, but encrypts the value before it enters the database, and decrypts it when it
retrieves it"""
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
super(EncryptedCharField, self).__init__(*args, **kwargs)
cipher_type = kwargs.pop('cipher', 'AES')
self.encryptor = Encryptor(cipher_type)

def get_prep_value(self, value):
return encrypt_if_not_encrypted(value, self.encryptor)

def to_python(self, value):
return decrypt_if_not_decrypted(value, self.encryptor)


def encrypt_if_not_encrypted(value, encryptor):
if isinstance(value, EncryptedString):
return value
else:
encrypted = encryptor.encrypt(value)
return EncryptedString(encrypted)

def decrypt_if_not_decrypted(value, encryptor):
if isinstance(value, DecryptedString):
return value
else:
encrypted = encryptor.decrypt(value)
return DecryptedString(encrypted)


class EncryptedString(str):
pass

class DecryptedString(str):
pass

加密器看起来像:

class Encryptor(object):
def __init__(self, cipher_type):
imp = __import__('Crypto.Cipher', globals(), locals(), [cipher_type], -1)
self.cipher = getattr(imp, cipher_type).new(settings.SECRET_KEY[:32])

def decrypt(self, value):
#values should always be encrypted no matter what!
#raise an error if tthings may have been tampered with
return self.cipher.decrypt(binascii.a2b_hex(str(value))).split('\0')[0]

def encrypt(self, value):
if value is not None and not isinstance(value, EncryptedString):
padding = self.cipher.block_size - len(value) % self.cipher.block_size
if padding and padding < self.cipher.block_size:
value += "\0" + ''.join([random.choice(string.printable) for index in range(padding-1)])
value = EncryptedString(binascii.b2a_hex(self.cipher.encrypt(value)))
return value

保存模型时,由于尝试解密已解密的字符串,出现错误“奇数长度字符串”。调试时,它显示为 to_python 最终被调用两次,第一次使用加密值,第二次使用解密值,但实际上不是作为 Decrypted 类型,而是作为原始字符串,导致错误。此外,永远不会调用 get_prep_value。

我做错了什么?

这不应该那么难——有没有人认为这个 Django 字段代码写得很糟糕,尤其是当涉及到自定义字段时,而且不是那么可扩展?简单的可重写的 pre_save 和 post_fetch 方法可以轻松解决这个问题。

最佳答案

我认为问题是当您为自定义字段赋值时也会调用 to_python(作为验证的一部分,可能是基于 this link )。所以问题是在以下情况下区分to_python调用:

  1. 当 Django 将数据库中的值分配给字段时(这就是您要解密该值的时候)
  2. 当您手动为自定义字段分配值时,例如record.field = 值

您可以使用的一个 hack 是向值字符串添加前缀或后缀并检查它而不是执行 isinstance 检查。

我本来打算写一个例子,但我找到了这个(甚至更好:))。

检查BaseEncryptedField: https://github.com/django-extensions/django-extensions/blob/2.2.9/django_extensions/db/fields/encrypted.py (链接到旧版本,因为该字段已在 3.0.0 中删除;请参阅 Issue #1359 了解弃用原因)

来源: Django Custom Field: Only run to_python() on values from DB?

关于python - 如何创建一个加密的 django 字段来转换从数据库中检索到的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13077109/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com