Django Secured Fields
Django encrypted fields with search enabled.
Requirements
- Python 3.10+
- Django 4.2+
- PostgreSQL, MySQL or SQLite
Features
- Automatically encrypt/decrypt field value using cryptography's Fernet
- Built-in search lookup on the encrypted fields from hashlib's SHA-256 hash value.
inandisnulllookup also supported. - Supports most of available Django fields including
BinaryField,JSONField, andFileField.
Installation
pip install django-secured-fields
Setup
-
Add
secured_fieldsintoINSTALLED_APPS# settings.py INSTALLED_APPS = [ ... 'secured_fields', ]
-
Generate a new key using for encryption
$ python manage.py generate_key KEY: TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg= HASH_SALT: 500d492e
-
Put generated key(s) and hash salt in settings
# settings.py SECURED_FIELDS_KEY = 'TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg=' # or multiple keys for rotation SECURED_FIELDS_KEY = [ 'TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg=', '...', ] # optional SECURED_FILDS_HASH_SALT = '500d492e'
Usage
Simple Usage
# models.py
import secured_fields
phone_number = secured_fields.EncryptedCharField(max_length=10)
Enable Searching
# models.py
import secured_fields
id_card_number = secured_fields.EncryptedCharField(max_length=18, searchable=True)
Supported Fields
EncryptedBinaryFieldEncryptedBooleanFieldEncryptedCharFieldEncryptedDateFieldEncryptedDateTimeFieldEncryptedDecimalFieldEncryptedFileFieldEncryptedImageFieldEncryptedIntegerFieldEncryptedJSONFieldEncryptedTextField
Settings
| Key | Required | Default | Description |
|---|---|---|---|
SECURED_FIELDS_KEY |
Yes | Key(s) for using in encryption/decryption with Fernet. Usually generated from python manage.py generate_key. For rotation keys, use a list of keys instead (see MultiFernet). |
|
SECURED_FIELDS_HASH_SALT |
No | '' |
Salt to append after the field value before hashing. Usually generated from python manage.py generate_key. |
SECURED_FIELDS_FILE_STORAGE |
No | 'secured_fields.storage.EncryptedFileSystemStorage' |
File storage class used for storing encrypted file/image fields. See EncryptedStorageMixin |
APIs
Field Arguments
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
searchable |
bool |
No | False |
Enable search function. exact/in lookups are only available when this is True; on a non-searchable field they raise LookupNotSupported. |
Changing searchable on a field with existing records
Existing records stay readable after changing searchable, but they keep the storage format
of the previous flag until they are re-saved:
False→True: existing records do not have a hashed section yet, soexact/inlookups will not match them.True→False: existing records still carry the old hashed section — a deterministic fingerprint of the value — until they are re-saved.
After changing the flag (and running makemigrations/migrate, since the database index changes),
re-save the affected records to convert them to the new format. Do this in a data migration that runs
after the generated AlterField operation — the historical model must already carry the new
searchable value, otherwise the records are silently re-written in the old format:
def resave_records(apps, schema_editor):
model = apps.get_model('myapp', 'MyModel')
model.objects.bulk_update(model.objects.all(), ['my_field'], batch_size=1000)
bulk_update re-encrypts through the same path as save() but batches the queries, and — unlike
re-saving each record — does not overwrite auto_now date/datetime fields with the migration run time.
Encryption
> from secured_fields.fernet import get_fernet
> data = b'test'
> encrypted_data = get_fernet().encrypt(data)
> encrypted_data
b'gAAAAABh2_Ry_thxLTuFFXeMc9hNttah82979JPuMSjnssRB0DmbgwdtEU5dapBgISOST_a_egDc66EG_ZtVu_EqF_69djJwuA=='
> get_fernet().decrypt(encrypted_data)
b'test'
Rotate Keys
> from secured_fields.fernet import get_fernet
> encrypted_data = get_fernet().encrypt(b'test')
> encrypted_data
b'gAAAAABh2_Ry_thxLTuFFXeMc9hNttah82979JPuMSjnssRB0DmbgwdtEU5dapBgISOST_a_egDc66EG_ZtVu_EqF_69djJwuA=='
> rotated_encrypted_data = get_fernet().rotate(encrypted_data)
> get_fernet().decrypt(rotated_encrypted_data)
b'test'
See more details in MultiFernet.rotate.
EncryptedMixin
If you have a field which is not supported by the package, you can use EncryptedMixin to enable encryption and search functionality for that custom field.
import secured_fields
from django.db import models
class EncryptedUUIDField(secured_fields.EncryptedMixin, models.UUIDField):
pass
task_id = EncryptedUUIDField(searchable=True)
EncryptedStorageMixin
If you use a custom file storage class (e.g. defined in settings.py's STORAGES), you can enable file encryption using EncryptedStorageMixin.
import secured_fields
from minio_storage.storage import MinioMediaStorage
class EncryptedMinioMediaStorage(
secured_fields.EncryptedStorageMixin,
MinioMediaStorage,
):
pass
Known Limitation
inlookup onJSONFieldis not available- Large files are not performance-friendly at the moment (see #2)
- Search on
BinaryFielddoes not supported at the moment (see #6) - Changing
searchableon a field with existing records requires re-saving the records to make search results consistent (see Changingsearchableon a field with existing records)
Development
Requirements
- Docker
- Poetry 2.0+
- MySQL Client
brew install mysql-clientecho 'export PATH="/usr/local/opt/mysql-client/bin:$PATH"' >> ~/.bash_profile
Running Project
-
Start backend databases
make up-db -
Run tests (see: Testing)
Linting
make lint
Testing
make test-pg # or make test-mysql, make test-sqlite
Fix Formatting
make yapf
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_secured_fields-0.5.0.tar.gz.
File metadata
- Download URL: django_secured_fields-0.5.0.tar.gz
- Upload date:
- Size: 11.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
poetry/2.3.3 CPython/3.13.15 Linux/6.17.0-1022-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
287a88103f0143a112fcb1899c96009ddbe2821e701e3f0aaebdb77e492e51cd
|
|
| MD5 |
55677cbc0bde08ae55a83a42b97e5cbc
|
|
| BLAKE2b-256 |
d6f38312da09c50ac6aef2ca400641bcb0dd10abd4cb2b4b21858244a0e0def9
|
File details
Details for the file django_secured_fields-0.5.0-py3-none-any.whl.
File metadata
- Download URL: django_secured_fields-0.5.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
poetry/2.3.3 CPython/3.13.15 Linux/6.17.0-1022-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b41bfc61840ecb9c2007ca75e28d3480c3b3b14f95485997e3974cbaa8cd59e9
|
|
| MD5 |
e1d6f6d599c73794085a26b6f83b9429
|
|
| BLAKE2b-256 |
ae2c57b3e8220bf585b669e64d4d409608d44ae938d944b402917c93f42b7cb2
|