Skip to content

C0D1UM/django-secured-fields

Repository files navigation

Django Secured Fields

GitHub GitHub Workflow Status codecov PyPI
PyPI - Python Version

Django encrypted fields with search enabled.

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. in and isnull lookup also supported.
  • Supports most of available Django fields including BinaryField, JSONField, and FileField.

Installation

pip install django-secured-fields

Setup

  1. Add secured_fields into INSTALLED_APPS

    # settings.py
    
    INSTALLED_APPS = [
        ...
        'secured_fields',
    ]
  2. Generate a new key using for encryption

    $ python manage.py generate_key
    KEY: TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg=
    HASH_SALT: 500d492e
  3. 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

  • EncryptedBinaryField
  • EncryptedBooleanField
  • EncryptedCharField
  • EncryptedDateField
  • EncryptedDateTimeField
  • EncryptedDecimalField
  • EncryptedFileField
  • EncryptedImageField
  • EncryptedIntegerField
  • EncryptedJSONField
  • EncryptedTextField

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

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 DEFAULT_FILE_STORAGE), 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

  • in lookup on JSONField is not available
  • Large files are not performance-friendly at the moment (see #2)
  • Search on BinaryField does not supported at the moment (see #6)
  • Changing searchable value in a field with the records in the database is not supported (see #7)

Development

Requirements

  • Docker
  • Poetry
  • MySQL Client
    • brew install mysql-client
    • echo 'export PATH="/usr/local/opt/mysql-client/bin:$PATH"' >> ~/.bash_profile

Running Project

  1. Start backend databases

    make up-db
  2. Run tests (see: Testing)

Linting

make lint

Testing

make test-pg  # or make test-mysql, make test-sqlite

Fix Formatting

make yapf

About

Django encrypted fields with search enabled.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •