Django model fields encrypted using Pycryptodome AES-256 GCM.
Django Searchable Encrypted Fields
This package is for you if you would like to encrypt model field data "in app" - ie before it is sent to the database.
Why another encrypted field package?
- We use AES-256 encryption with GCM mode (via the Pycryptodome library).
- There is a django command to easily generate appropriate encryption keys.
- You can make 'exact' search lookups when also using the SearchField.
Install & Setup
$ pip install django-searchable-encrypted-fields
# in settings.py INSTALLED_APPS += ["encrypted_fields"] # A list of hex-encoded 32 byte keys # You only need one unless/until rotating keys FIELD_ENCRYPTION_KEYS = [ "f164ec6bd6fbc4aef5647abc15199da0f9badcc1d2127bde2087ae0d794a9a0b" ]
This package provides two types of model field for Django.
- A series of EncryptedField classes which can be used by themselves and work just like their regular Django counterparts. Contents are transparently encrypted/decrypted.
- A SearchField which can be used in conjunction with any EncryptedField. Values are concatentaed with a
hash_keyand then hashed with SHA256 before storing in a separate field. This means 'exact' searches can be performed.
This is probably best demonstrated by example:
Using a stand-alone EncryptedField
from encrypted_fields import fields class Person(models.Model): favorite_number = fields.EncryptedIntegerField(help_text="Your favorite number.")
You can use all the usual field arguments and add validators as normal. Note, however, that primary_key, unique and db_index are not supported because they do not make sense for encrypted data.
Using a SearchField along with an EncryptedField
class Person(models.Model): _name_data = fields.EncryptedCharField(max_length=50, editable=False) name = fields.SearchField(encrypted_data_field="_name_data", hash_key="f164ec6bd...7ae0d794a9a0b") favorite_number = fields.EncryptedIntegerField() city = models.CharField(max_length=255) # regular Django model field
You can then use it like:
# "Jo" is hashed and stored in 'name' as well as symmetrically encrypted and stored in '_name_data' Person.objects.create(name="Jo", favorite_number=7, city="London") person = Person.objects.get(name="Jo") assert person.name == "Jo" assert person.favorite_number == 7 person = Person.objects.get(city="London") assert person.name == "Jo" . # the data is taken from '_name_data', which decrypts it first.
You can safely update like this:
person.name = "Simon" person.save()
But when using
update() you need to provide the value to both fields:
A SearchField inherits the validators and formfield (widget) from its associated EncryptedField. So:
- Do not add validators or form widgets to SearchFields (they will be ignored), add them to the associated EncryptedField instead.
- Do not include the EncryptedField in forms, instead just display the SearchField.
Included EncryptedField classes
The following are included:
"EncryptedFieldMixin", "EncryptedTextField", "EncryptedCharField", "EncryptedEmailField", "EncryptedIntegerField", "EncryptedDateField", "EncryptedDateTimeField", "EncryptedBigIntegerField", "EncryptedPositiveIntegerField", "EncryptedPositiveSmallIntegerField", "EncryptedSmallIntegerField",
Note that, although untested, you should be able to extend other regular Django model field classes like this:
class EncryptedIPAddressField(EncryptedFieldMixin, models.GenericIPAddressField): pass
Please let us know if you have problems when doing this.
Generating Encryption Keys
You can use the included Django management command which will print appropriate hex-encoded keys to the terminal, ready to be used in
settings.FIELD_ENCRYPTION_KEYS or as a hash_key for a SearchField:
$ python manage.py generate_key
Note: encryption keys must be hex encoded and 32 bytes
Important: use different hash_key values for each SearchField and make sure they are different from any keys in
Rotating Encryption Keys
If you want to rotate the encryption key just prepend
settings.FIELD_ENCRYPTION_KEYS with a new key. This new key (the first in the list) will be used for encrypting/decrypting all data. If decrypting data fails (because it was encrypted with an older key), each key in the list is tried.
django-searchable-encrypted-fields is tested with Django(2.1, 2.2) on Python(3.6, 3.7) using SQLite and PostgreSQL.
Test coverage is at 96%.