Configuring Queryable Encryption¶
Added in version 5.2.3.
Queryable Encryption is a powerful MongoDB feature that allows you to encrypt sensitive fields in your database while still supporting queries on that encrypted data.
This section will guide you through the process of configuring Queryable Encryption in your Django project.
MongoDB requirements
Queryable Encryption can be used with MongoDB replica sets or sharded clusters running version 8.0 or later. Standalone instances are not supported. The Queryable Encryption Compatibility table summarizes which MongoDB server products support Queryable Encryption.
Installation¶
In addition to Django MongoDB Backend’s regular installation and configuration steps, Queryable Encryption has additional Python dependencies:
$ pip install django-mongodb-backend[encryption]
Configuring the DATABASES setting¶
In addition to the database settings
required to use Django MongoDB Backend, Queryable Encryption requires you to
configure a separate encrypted database connection in your
DATABASES setting.
Encrypted database
An encrypted database is a separate database connection in your
DATABASES setting that is configured to use PyMongo’s
automatic encryption.
Here’s how to configure an encrypted database using a local KMS provider and
encryption keys stored in the encryption.__keyVault collection:
import os
from pymongo.encryption_options import AutoEncryptionOpts
DATABASES = {
"default": {
"ENGINE": "django_mongodb_backend",
"HOST": "mongodb+srv://cluster0.example.mongodb.net",
"NAME": "my_database",
# ...
},
"encrypted": {
"ENGINE": "django_mongodb_backend",
"HOST": "mongodb+srv://cluster0.example.mongodb.net",
"NAME": "my_database_encrypted",
"USER": "my_user",
"PASSWORD": "my_password",
"PORT": 27017,
"OPTIONS": {
"auto_encryption_opts": AutoEncryptionOpts(
key_vault_namespace="encryption.__keyVault",
kms_providers={"local": {"key": os.urandom(96)}},
)
},
},
}
Local KMS provider key
In the example above, a random key is generated for the local KMS provider
using os.urandom(96). In a production environment, you should securely
store and manage your encryption keys.
Configuring the DATABASE_ROUTERS setting¶
Similar to configuring the DATABASE_ROUTERS setting for
embedded models, Queryable Encryption
requires a DATABASE_ROUTERS setting to
route database operations to the encrypted database.
The following example shows how to configure a router for the “myapp” application that routes database operations to the encrypted database for all models in that application:
# myapp/routers.py
class EncryptedRouter:
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label == "myapp":
return db == "encrypted"
# Prevent migrations on the encrypted database for other apps
if db == "encrypted":
return False
return None
def db_for_read(self, model, **hints):
if model._meta.app_label == "myapp":
return "encrypted"
return None
db_for_write = db_for_read
Then in your Django settings, add the custom database router to the
DATABASE_ROUTERS setting:
# settings.py
DATABASE_ROUTERS = ["myapp.routers.EncryptedRouter"]
Configuring the Key Management Service (KMS)¶
To use Queryable Encryption, you must configure a Key Management Service (KMS) to store and manage your encryption keys. Django MongoDB Backend allows you to configure multiple KMS providers and select the appropriate provider for each model using a custom database router.
The KMS is responsible for managing the encryption keys used to encrypt and decrypt data. The following table summarizes the available KMS configuration options followed by an example of how to use them.
A dictionary of Key Management Service (KMS)
credentials configured in the
|
|
A dictionary of KMS provider credentials used to
access the KMS with |
|
A single KMS provider name configured in your custom database router. |
Example of KMS configuration with aws in your kms_providers setting:
from pymongo.encryption_options import AutoEncryptionOpts
DATABASES = {
"encrypted": {
# ...
"OPTIONS": {
"auto_encryption_opts": AutoEncryptionOpts(
# ...
kms_providers={
"aws": {
"accessKeyId": "your-access-key-id",
"secretAccessKey": "your-secret-access-key",
},
},
),
},
"KMS_CREDENTIALS": {
"aws": {
"key": os.getenv("AWS_KEY_ARN", ""),
"region": os.getenv("AWS_KEY_REGION", ""),
},
},
},
}
(TODO: If there’s a use case for multiple providers, motivate with a use case and add a test.)
If you’ve configured multiple KMS providers, you must define logic to determine the provider for each model in your database router:
class EncryptedRouter:
# ...
def kms_provider(self, model, **hints):
return "aws"
Configuring the encrypted_fields_map option¶
When you configure the DATABASES
setting for Queryable Encryption without specifying an
encrypted_fields_map, Django MongoDB Backend will create encrypted
collections, including encryption keys, when you run migrations for models
that have encrypted fields.
Encryption keys for encrypted fields are stored in the key vault specified in
the DATABASES setting. To see the keys created by
Django MongoDB Backend, along with the entire schema, you can run the
showencryptedfieldsmap command:
$ python manage.py showencryptedfieldsmap --database encrypted
Use the output of showencryptedfieldsmap to set the
encrypted_fields_map in AutoEncryptionOpts in your Django settings:
from bson import json_util
from pymongo.encryption_options import AutoEncryptionOpts
DATABASES = {
"encrypted": {
# ...
"OPTIONS": {
"auto_encryption_opts": AutoEncryptionOpts(
# ...
encrypted_fields_map=json_util.loads(
"""{
"encrypt_patient": {
"fields": [
{
"bsonType": "string",
"path": "patient_record.ssn",
"keyId": {
"$binary": {
"base64": "2MA29LaARIOqymYHGmi2mQ==",
"subType": "04"
}
},
"queries": {
"queryType": "equality"
}
},
]
}}"""
),
),
},
},
}
Security consideration
Supplying an encrypted fields map provides more security than relying on an encrypted fields map obtained from the server. It protects against a malicious server advertising a false encrypted fields map.