django-secured-fields


Namedjango-secured-fields JSON
Version 0.4.4 PyPI version JSON
download
home_pagehttps://github.com/C0D1UM/django-secured-fields
SummaryNone
upload_time2024-11-01 21:37:36
maintainerNone
docs_urlNone
authorCODIUM
requires_python<4.0,>=3.8
licenseMIT
keywords django encrypted fields drf django-rest restframework search encryption hash
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Django Secured Fields

[![GitHub](https://img.shields.io/github/license/C0D1UM/django-secured-fields)](https://github.com/C0D1UM/django-secured-fields/blob/main/LICENSE)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/C0D1UM/django-secured-fields/ci.yml?branch=main)](https://github.com/C0D1UM/django-secured-fields/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/C0D1UM/django-secured-fields/branch/main/graph/badge.svg?token=PN19DJ3SDF)](https://codecov.io/gh/C0D1UM/django-secured-fields)
[![PyPI](https://img.shields.io/pypi/v/django-secured-fields)](https://pypi.org/project/django-secured-fields/)  
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-secured-fields)](https://github.com/C0D1UM/django-secured-fields)

Django encrypted fields with search enabled.

## Features

- Automatically encrypt/decrypt field value using [cryptography](https://github.com/pyca/cryptography)'s [Fernet](https://cryptography.io/en/latest/fernet)
- Built-in search lookup on the encrypted fields from [hashlib](https://docs.python.org/3/library/hashlib.html)'s _SHA-256_ hash value. `in` and `isnull` lookup also supported.
- Supports most of available Django fields including `BinaryField`, `JSONField`, and `FileField`.

## Installation

```bash
pip install django-secured-fields
```

## Setup

1. Add `secured_fields` into `INSTALLED_APPS`

   ```python
   # settings.py

   INSTALLED_APPS = [
       ...
       'secured_fields',
   ]
   ```

2. Generate a new key using for encryption

   ```bash
   $ python manage.py generate_key
   KEY: TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg=
   HASH_SALT: 500d492e
   ```

3. Put generated key(s) and hash salt in settings

   ```python
   # 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

```python
# models.py
import secured_fields

phone_number = secured_fields.EncryptedCharField(max_length=10)
```

### Enable Searching

```python
# 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](https://cryptography.io/en/latest/fernet/#cryptography.fernet.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](#encryptedstoragemixin) |

## APIs

### Field Arguments

| Name | Type | Required | Default | Description |
| ---- | ---- | -------- | ------- | ----------- |
| `searchable` | `bool` | No | `False` | Enable search function |

### Encryption

```python
> 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

```python
> 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](https://cryptography.io/en/latest/fernet/#cryptography.fernet.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.

```python
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`.

```python
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](https://github.com/C0D1UM/django-secured-fields/issues/2))
- Search on `BinaryField` does not supported at the moment (see [#6](https://github.com/C0D1UM/django-secured-fields/issues/6))
- Changing `searchable` value in a field with the records in the database is not supported (see [#7](https://github.com/C0D1UM/django-secured-fields/issues/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

   ```bash
   make up-db
   ```

2. Run tests (see: [Testing](#testing))

### Linting

```bash
make lint
```

### Testing

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

### Fix Formatting

```bash
make yapf
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/C0D1UM/django-secured-fields",
    "name": "django-secured-fields",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.8",
    "maintainer_email": null,
    "keywords": "django, encrypted, fields, drf, django-rest, restframework, search, encryption, hash",
    "author": "CODIUM",
    "author_email": "support@codium.co",
    "download_url": "https://files.pythonhosted.org/packages/10/24/3d1e6587ed934165e4c21e9bfcc5e707ee250ffcb7aae9993d82d17fb3c6/django_secured_fields-0.4.4.tar.gz",
    "platform": null,
    "description": "# Django Secured Fields\n\n[![GitHub](https://img.shields.io/github/license/C0D1UM/django-secured-fields)](https://github.com/C0D1UM/django-secured-fields/blob/main/LICENSE)\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/C0D1UM/django-secured-fields/ci.yml?branch=main)](https://github.com/C0D1UM/django-secured-fields/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/C0D1UM/django-secured-fields/branch/main/graph/badge.svg?token=PN19DJ3SDF)](https://codecov.io/gh/C0D1UM/django-secured-fields)\n[![PyPI](https://img.shields.io/pypi/v/django-secured-fields)](https://pypi.org/project/django-secured-fields/)  \n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-secured-fields)](https://github.com/C0D1UM/django-secured-fields)\n\nDjango encrypted fields with search enabled.\n\n## Features\n\n- Automatically encrypt/decrypt field value using [cryptography](https://github.com/pyca/cryptography)'s [Fernet](https://cryptography.io/en/latest/fernet)\n- Built-in search lookup on the encrypted fields from [hashlib](https://docs.python.org/3/library/hashlib.html)'s _SHA-256_ hash value. `in` and `isnull` lookup also supported.\n- Supports most of available Django fields including `BinaryField`, `JSONField`, and `FileField`.\n\n## Installation\n\n```bash\npip install django-secured-fields\n```\n\n## Setup\n\n1. Add `secured_fields` into `INSTALLED_APPS`\n\n   ```python\n   # settings.py\n\n   INSTALLED_APPS = [\n       ...\n       'secured_fields',\n   ]\n   ```\n\n2. Generate a new key using for encryption\n\n   ```bash\n   $ python manage.py generate_key\n   KEY: TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg=\n   HASH_SALT: 500d492e\n   ```\n\n3. Put generated key(s) and hash salt in settings\n\n   ```python\n   # settings.py\n\n   SECURED_FIELDS_KEY = 'TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg='\n   # or multiple keys for rotation\n   SECURED_FIELDS_KEY = [\n       'TtY8MAeXuhdKDd1HfGUwim-vQ8H7fXyRQ9J8pTi_-lg=',\n       '...',\n   ]\n\n   # optional\n   SECURED_FILDS_HASH_SALT = '500d492e'\n   ```\n\n## Usage\n\n### Simple Usage\n\n```python\n# models.py\nimport secured_fields\n\nphone_number = secured_fields.EncryptedCharField(max_length=10)\n```\n\n### Enable Searching\n\n```python\n# models.py\nimport secured_fields\n\nid_card_number = secured_fields.EncryptedCharField(max_length=18, searchable=True)\n```\n\n## Supported Fields\n\n- `EncryptedBinaryField`\n- `EncryptedBooleanField`\n- `EncryptedCharField`\n- `EncryptedDateField`\n- `EncryptedDateTimeField`\n- `EncryptedDecimalField`\n- `EncryptedFileField`\n- `EncryptedImageField`\n- `EncryptedIntegerField`\n- `EncryptedJSONField`\n- `EncryptedTextField`\n\n## Settings\n\n| Key | Required | Default | Description |\n| --- | -------- | ------- | ----------- |\n| `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](https://cryptography.io/en/latest/fernet/#cryptography.fernet.MultiFernet)). |\n| `SECURED_FIELDS_HASH_SALT` | No | `''` | Salt to append after the field value before hashing. Usually generated from `python manage.py generate_key`. |\n| `SECURED_FIELDS_FILE_STORAGE` | No | `'secured_fields.storage.EncryptedFileSystemStorage'` | File storage class used for storing encrypted file/image fields. See [EncryptedStorageMixin](#encryptedstoragemixin) |\n\n## APIs\n\n### Field Arguments\n\n| Name | Type | Required | Default | Description |\n| ---- | ---- | -------- | ------- | ----------- |\n| `searchable` | `bool` | No | `False` | Enable search function |\n\n### Encryption\n\n```python\n> from secured_fields.fernet import get_fernet\n\n> data = b'test'\n\n> encrypted_data = get_fernet().encrypt(data)\n> encrypted_data\nb'gAAAAABh2_Ry_thxLTuFFXeMc9hNttah82979JPuMSjnssRB0DmbgwdtEU5dapBgISOST_a_egDc66EG_ZtVu_EqF_69djJwuA=='\n\n> get_fernet().decrypt(encrypted_data)\nb'test'\n```\n\n### Rotate Keys\n\n```python\n> from secured_fields.fernet import get_fernet\n\n> encrypted_data = get_fernet().encrypt(b'test')\n> encrypted_data\nb'gAAAAABh2_Ry_thxLTuFFXeMc9hNttah82979JPuMSjnssRB0DmbgwdtEU5dapBgISOST_a_egDc66EG_ZtVu_EqF_69djJwuA=='\n\n> rotated_encrypted_data = get_fernet().rotate(encrypted_data)\n> get_fernet().decrypt(rotated_encrypted_data)\nb'test'\n```\n\nSee more details in [MultiFernet.rotate](https://cryptography.io/en/latest/fernet/#cryptography.fernet.MultiFernet.rotate).\n\n### `EncryptedMixin`\n\nIf 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.\n\n```python\nimport secured_fields\nfrom django.db import models\n\nclass EncryptedUUIDField(secured_fields.EncryptedMixin, models.UUIDField):\n    pass\n\ntask_id = EncryptedUUIDField(searchable=True)\n```\n\n### `EncryptedStorageMixin`\n\nIf you use a custom file storage class (e.g. defined in `settings.py`'s `DEFAULT_FILE_STORAGE`), you can enable file encryption using `EncryptedStorageMixin`.\n\n```python\nimport secured_fields\nfrom minio_storage.storage import MinioMediaStorage\n\nclass EncryptedMinioMediaStorage(\n    secured_fields.EncryptedStorageMixin,\n    MinioMediaStorage,\n):\n    pass\n```\n\n## Known Limitation\n\n- `in` lookup on `JSONField` is not available\n- Large files are not performance-friendly at the moment (see [#2](https://github.com/C0D1UM/django-secured-fields/issues/2))\n- Search on `BinaryField` does not supported at the moment (see [#6](https://github.com/C0D1UM/django-secured-fields/issues/6))\n- Changing `searchable` value in a field with the records in the database is not supported (see [#7](https://github.com/C0D1UM/django-secured-fields/issues/7))\n\n## Development\n\n### Requirements\n\n- Docker\n- Poetry\n- MySQL Client\n  - `brew install mysql-client`\n  - `echo 'export PATH=\"/usr/local/opt/mysql-client/bin:$PATH\"' >> ~/.bash_profile`\n\n### Running Project\n\n1. Start backend databases\n\n   ```bash\n   make up-db\n   ```\n\n2. Run tests (see: [Testing](#testing))\n\n### Linting\n\n```bash\nmake lint\n```\n\n### Testing\n\n```bash\nmake test-pg  # or make test-mysql, make test-sqlite\n```\n\n### Fix Formatting\n\n```bash\nmake yapf\n```\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": null,
    "version": "0.4.4",
    "project_urls": {
        "Homepage": "https://github.com/C0D1UM/django-secured-fields",
        "Repository": "https://github.com/C0D1UM/django-secured-fields"
    },
    "split_keywords": [
        "django",
        " encrypted",
        " fields",
        " drf",
        " django-rest",
        " restframework",
        " search",
        " encryption",
        " hash"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "59aad6c7565e7842bcd97883d282b8356855b319116eaab6de0ed06b57d55632",
                "md5": "994aa5723695d092090c30f6742cd1fe",
                "sha256": "7cb613587b52976f6771a8190f46219d5357955e220d8d7ae4a6b5fc7f4503ff"
            },
            "downloads": -1,
            "filename": "django_secured_fields-0.4.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "994aa5723695d092090c30f6742cd1fe",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.8",
            "size": 10344,
            "upload_time": "2024-11-01T21:37:34",
            "upload_time_iso_8601": "2024-11-01T21:37:34.505979Z",
            "url": "https://files.pythonhosted.org/packages/59/aa/d6c7565e7842bcd97883d282b8356855b319116eaab6de0ed06b57d55632/django_secured_fields-0.4.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "10243d1e6587ed934165e4c21e9bfcc5e707ee250ffcb7aae9993d82d17fb3c6",
                "md5": "a1a29fbda8df484430a5c5a4bfbfd784",
                "sha256": "8a2edcbf8b3efaf89a592caf748b80f8f38a7c253ec45d7526bf345380aca3ad"
            },
            "downloads": -1,
            "filename": "django_secured_fields-0.4.4.tar.gz",
            "has_sig": false,
            "md5_digest": "a1a29fbda8df484430a5c5a4bfbfd784",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.8",
            "size": 9275,
            "upload_time": "2024-11-01T21:37:36",
            "upload_time_iso_8601": "2024-11-01T21:37:36.262947Z",
            "url": "https://files.pythonhosted.org/packages/10/24/3d1e6587ed934165e4c21e9bfcc5e707ee250ffcb7aae9993d82d17fb3c6/django_secured_fields-0.4.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-01 21:37:36",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "C0D1UM",
    "github_project": "django-secured-fields",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "django-secured-fields"
}
        
Elapsed time: 2.76386s