{"id":4506,"library":"django-fernet-fields-v2","title":"Fernet-encrypted model fields for Django","description":"django-fernet-fields-v2 provides Fernet symmetric encryption for Django model fields, leveraging the `cryptography` library. It is a fork of `django-fernet-fields`, specifically updated to support Django 4 and later versions, as well as Python 3.8+. The current version is 0.9, released in August 2023, with releases typically occurring on an as-needed basis to maintain compatibility.","status":"active","version":"0.9","language":"en","source_language":"en","source_url":"https://github.com/MichelML/django-fernet-fields","tags":["django","security","encryption","fernet","fields","data-at-rest"],"install":[{"cmd":"pip install django-fernet-fields-v2","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Required for Django model integration.","package":"Django","optional":false},{"reason":"Provides the underlying Fernet encryption capabilities.","package":"cryptography","optional":false}],"imports":[{"symbol":"FernetCharField","correct":"from fernet_fields import FernetCharField"},{"symbol":"FernetTextField","correct":"from fernet_fields import FernetTextField"},{"symbol":"FernetEmailField","correct":"from fernet_fields import FernetEmailField"},{"symbol":"FernetIntegerField","correct":"from fernet_fields import FernetIntegerField"},{"symbol":"FernetDateField","correct":"from fernet_fields import FernetDateField"},{"symbol":"FernetDateTimeField","correct":"from fernet_fields import FernetDateTimeField"}],"quickstart":{"code":"import os\nfrom django.db import models\nfrom django.conf import settings\nfrom fernet_fields import FernetTextField\n\n# Minimum Django settings for model use\nsettings.configure(\n    SECRET_KEY=os.environ.get('DJANGO_SECRET_KEY', 'a-very-secret-key-for-testing-only-do-not-use-in-prod'),\n    DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},\n    INSTALLED_APPS=['fernet_fields'],\n)\n\n# Define a model with an encrypted text field\nclass MyEncryptedModel(models.Model):\n    secret_data = FernetTextField()\n\n    def __str__(self):\n        return f\"Encrypted ID: {self.id}\"\n\n# Example usage (after migrations, typically via manage.py)\n# In a real application, you would run makemigrations and migrate.\n# For this quickstart, we'll simulate it for demonstration.\nif not settings.configured:\n    settings.configure(DEBUG=True, SECRET_KEY='dummy-key', INSTALLED_APPS=['fernet_fields'])\n\n# Note: In a real Django project, you would create and apply migrations.\n# For a standalone runnable example, we bypass direct model setup.\n# from django.apps import apps; apps.populate(settings.INSTALLED_APPS)\n\n# This part requires a proper Django setup with migrations applied.\n# For demonstration, assume model and database are ready.\n# from django.db import connection\n# with connection.schema_editor() as schema_editor:\n#     schema_editor.create_model(MyEncryptedModel)\n\n# Demonstrate field usage (conceptually)\n# If MyEncryptedModel was properly migrated:\n# instance = MyEncryptedModel.objects.create(secret_data='This is highly sensitive information.')\n# print(f\"Saved encrypted data. Retrieved: {instance.secret_data}\")\n# print(f\"Data in DB (would be encrypted): {MyEncryptedModel.objects.get(id=instance.id).secret_data}\")\n\nprint(\"To run this, integrate into a Django project, define FERNET_KEYS or SECRET_KEY, and apply migrations.\")\nprint(\"Example model `MyEncryptedModel` defined with `secret_data = FernetTextField()`\")","lang":"python","description":"To use `django-fernet-fields-v2`, define encrypted fields in your Django models by importing them from `fernet_fields`. Ensure your `settings.py` includes a `SECRET_KEY` (which it uses by default) or `FERNET_KEYS` for explicit key management. Then, create and apply migrations as usual. Data assigned to these fields will be automatically encrypted before saving to the database and decrypted upon retrieval."},"warnings":[{"fix":"Securely store your `SECRET_KEY` and any `FERNET_KEYS` in environment variables or a secrets management system. Implement robust backup strategies for your keys.","message":"Loss of encryption keys will result in permanent data loss. If the `SECRET_KEY` (or any key in `FERNET_KEYS`) used to encrypt data is lost, that data becomes irrecoverable.","severity":"breaking","affected_versions":"All versions"},{"fix":"Avoid applying `db_index=True` or `unique=True` to Fernet fields. For searchability or uniqueness, consider storing a hashed, non-sensitive version of the data in a separate, non-encrypted field, or design your application to retrieve and filter data in memory after decryption.","message":"Fernet encryption is non-deterministic, meaning the same plaintext encrypts to different ciphertext each time. This makes database indexing, unique constraints, and direct lookups (e.g., `filter(field='value')`) on encrypted fields impossible or meaningless, and can raise `django.core.exceptions.ValidationError` or similar errors if attempted.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For full key rotation, you must perform a data migration. This typically involves iterating through all instances of models with encrypted fields, re-saving them (`instance.save()`) after the new key is placed at the head of `FERNET_KEYS`. Ensure old keys remain in `FERNET_KEYS` (after the new key) until all data is re-encrypted to allow decryption of existing values during the transition.","message":"When rotating encryption keys using `FERNET_KEYS`, new data will be encrypted with the first key in the list, but old data must be manually re-encrypted if you want it to use the new key for future saves. Simply changing `FERNET_KEYS` will not automatically re-encrypt existing data in the database.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Increase the `max_length` of `FernetCharField` fields beyond what would be needed for the plaintext. A common recommendation is to allow for at least 2x-3x the original length, plus a small overhead (e.g., if plaintext is 255 chars, use 765 or more).","message":"Encrypted data is generally longer than its plaintext counterpart. If you use `FernetCharField`, you must ensure `max_length` is sufficient to accommodate the encrypted string, otherwise data truncation or validation errors may occur.","severity":"gotcha","affected_versions":"All versions"},{"fix":"If this information leakage is a concern, avoid nullable encrypted fields. Instead, store a specific 'empty' sentinel value (which will be encrypted like any other data) in a non-nullable encrypted field.","message":"Using nullable encrypted fields (`blank=True, null=True`) can inadvertently leak information about the presence or absence of data, as a `NULL` in the database trivially indicates an empty field.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-12T00:00:00.000Z","next_check":"2026-07-11T00:00:00.000Z"}