Ho un EncryptedCharField personalizzato, che voglio fondamentalmente apparire come un CharField quando si interfaccia l'interfaccia utente, ma prima di archiviare/recuperare nel DB lo crittografa/decrittografa.Come posso creare un campo django criptato che converte i dati quando vengono recuperati dal database?
La custom fields documentation dice:
- aggiungere
__metaclass__ = models.SubfieldBase
- esclusione to_python per convertire i dati da esso è formato grezzo nel formato desiderato
- esclusione get_prep_value per convertire il valore prima di memorizzare ot il db.
Quindi pensi che sarebbe abbastanza facile - per 2. decrittografare il valore e 3. basta crittografarlo.
liberamente ispirato a django snippet, e la documentazione questo campo assomiglia:
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
e l'Encryptor assomiglia:
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
Quando si salva un modello, un errore, stringa di Odd-length, si verifica, come risultato del tentativo di decrittografare una stringa già decrittografata. Quando esegue il debug, appare come to_python che viene chiamato due volte, il primo con il valore crittografato e la seconda volta con il valore decrittografato, ma non in realtà come un tipo Decrittografato, ma come una stringa non elaborata, che causa l'errore. Inoltre, get_prep_value non viene mai chiamato.
Cosa sto sbagliando?
Questo non dovrebbe essere così difficile - qualcun altro pensa che questo codice di campo Django sia scritto molto male, specialmente quando si tratta di campi personalizzati, e non così estensibile? Semplici metodi pre_save e post_fetch sovrascrivibili risolvono facilmente questo problema.
Posso chiedere come hai fatto risolto, perché ho bisogno di fare qualcosa di simile a un 'IntegerField'. Questa è una vecchia domanda, quindi stai ancora usando questa crittografia personalizzata o qualche applicazione di terze parti? – PetarP