カスタムの EncryptedCharField があります。これは、UI とのインターフェイス時に基本的に CharField として表示されますが、DB に格納/取得する前に暗号化/復号化します。
カスタム フィールドのドキュメントには次のように記載されています。
- 追加
__metaclass__ = models.SubfieldBase
- to_python をオーバーライドして、未加工のストレージからデータを目的の形式に変換します
- get_prep_value をオーバーライドして、データベースに保存する前に値を変換します。
したがって、これは簡単だと思います-2.値を復号化するだけで、3.それを暗号化するだけです。
大まかに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
Encryptor は次のようになります。
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 が 2 回呼び出されたように見えます。1 回目は暗号化された値で、2 回目は復号化された値で呼び出されますが、実際には Decrypted 型としてではなく、生の文字列としてエラーが発生します。さらに、get_prep_value が呼び出されることはありません。
私は何を間違っていますか?
これはそれほど難しいことではありません。この Django フィールド コードが非常に貧弱に書かれていて、特にカスタム フィールドに関しては、それほど拡張可能ではないと考える人はいますか? シンプルなオーバーライド可能な pre_save および post_fetch メソッドを使用すると、この問題を簡単に解決できます。