Docs Menu
Docs Home
/ /

Tutorial: Queryable Encryption with Django MongoDB Backend

In this tutorial, you can learn how to use Queryable Encryption (QE) in a Django application.

MongoDB's Queryable Encryption feature helps protect sensitive data by automatically encrypting it while still allowing your application to interact with the encrypted data. In this tutorial, you can learn how to use Queryable Encryption with Django MongoDB Backend by enabling encryption in your application settings and choosing model fields to encrypt.

Before you begin this tutorial, complete the following prerequisite tasks:

  • Create a MongoDB Atlas account and configure a cluster. Ensure that your cluster runs on MongoDB Server version 7.0 or later. To learn more, see the MongoDB Get Started guide.

  • Download the Automatic Encryption Shared Library. To view instructions, see the Install and Configure a Query Analysis Component guide. These instructions show how to navigate to the MongoDB Download Center and fill out the form required to download the library.

  • Install Python 3.10 or later.

In this tutorial, you create a Queryable Encryption application that uses Django MongoDB Backend. The application handles medical records securely: it encrypts, decrypts, and queries sensitive medical information.

Tip

Complete Application

To view the complete sample application for this tutorial, see the django_qe folder on GitHub.

Follow the steps in this section to install the project dependencies, configure your environment, and create the application structure.

1

Select the tab corresponding to your operating system and run the following commands to activate a virtual environment, install Django MongoDB Backend, and install the Queryable Encryption dependencies:

python -m venv venv
source venv/bin/activate
pip install 'django-mongodb-backend[encryption]'
python -m venv venv
. venv\Scripts\activate
pip install 'django-mongodb-backend[encryption]'

The pip install 'django-mongodb-backend[encryption]' command installs Django MongoDB Backend, the latest PyMongo version, and the required Python dependencies.

Note

Available in v6.0.1

Queryable Encryption requires Django MongoDB Backend v6.0.1 or later. The preceding command installs the required version.

2

From your shell, run the following command to create a new Django project called django_qe based on a custom template:

django-admin startproject django_qe --template https://github.com/mongodb-labs/django-mongodb-project/archive/refs/heads/6.0.x.zip

This command creates a django_qe project directory and adds the initial file scaffolding.

3

From your django_qe directory, run the following command to create an application called medical_records:

python manage.py startapp medical_records

This command creates a medical_records application directory in your project and adds default app files, including files for your models and settings.

4

Navigate to the settings.py file in your django_qe directory and edit your INSTALLED_APPS setting to resemble the following code:

INSTALLED_APPS = [
'medical_records.apps.MedicalRecordsConfig',
'django_qe.apps.MongoAdminConfig',
'django_qe.apps.MongoAuthConfig',
'django_qe.apps.MongoContentTypesConfig',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_mongodb_backend',
]

This setting ensures that your medical_records app is enabled in your project.

After setting up your project, follow the steps in this section to connect to MongoDB and create models that store encrypted data.

1

In your django_qe directory, navigate to the settings.py file. Replace the DATABASES setting with the following code:

DATABASES = {
'default': {
'ENGINE': 'django_mongodb_backend',
'HOST': '<connection string>',
'NAME': 'db',
},
}

Replace the <connection string> placeholder with the connection string that connects to your cluster.

This code configures your Django application to connect to MongoDB and access a database named db by default. This configuration applies to your non-encrypted models.

2

In your settings.py file, you can specify the encrypted key in your DATABASES setting to enable Queryable Encryption and set your Key Management System (KMS) provider. Django MongoDB Backend supports the AWS, Azure, GCP, KMIP, and local KMS providers.

Warning

Local CMK Storage

If you store your Customer Master Key (CMK) locally, do not use this application in production. Without a remote KMS, you risk unauthorized access to the encryption key or loss of the key needed to decrypt your data.

Tip

Key Management Systems

To learn more about KMS, see the Key Management Wikipedia entry.

Select the tab corresponding to your KMS and modify your DATABASES setting to resemble the following example:

DATABASES = {
'default': {
# ... Your default configuration
},
'encrypted': {
'ENGINE': 'django_mongodb_backend',
'HOST': '<connection string>',
'NAME': 'medical_records',
# ...
'OPTIONS': {
'auto_encryption_opts': AutoEncryptionOpts(
key_vault_namespace='medical_records.__keyVault',
kms_providers={
'local': {
# Generated by os.urandom(96)
'key': (
b'-\xc3\x0c\xe3\x93\xc3\x8b\xc0\xf8\x12\xc5#b'
b'\x19\xf3\xbc\xccR\xc8\xedI\xda\\ \xfb\x9cB'
b'\x7f\xab5\xe7\xb5\xc9x\xb8\xd4d\xba\xdc\x9c'
b'\x9a\xdb9J]\xe6\xce\x104p\x079q.=\xeb\x9dK*'
b'\x97\xea\xf8\x1e\xc3\xd49K\x18\x81\xc3\x1a"'
b'\xdc\x00U\xc4u"X\xe7xy\xa5\xb2\x0e\xbc\xd6+-'
b'\x80\x03\xef\xc2\xc4\x9bU'
)
},
},
crypt_shared_lib_path='<Automatic Encryption Shared Library path>',
crypt_shared_lib_required=True,
)
},
},
}

Replace the following placeholder values:

  • <connection string>: The same connection string you specified in the default key

  • <Automatic Encryption Shared Library path>: The full path to your Automatic Encryption Shared Library

DATABASES = {
'default': {
# ... Your default configuration
},
'encrypted': {
'ENGINE': 'django_mongodb_backend',
'HOST': '<connection string>',
'NAME': 'medical_records',
# ...
'OPTIONS': {
'auto_encryption_opts': AutoEncryptionOpts(
key_vault_namespace='medical_records.__keyVault',
kms_providers={
'aws': {
'accessKeyId': '<AWS Access Key ID>',
'secretAccessKey': '<AWS Secret Access Key>',
},
},
crypt_shared_lib_path='<Automatic Encryption Shared Library path>',
crypt_shared_lib_required=True,
)
},
'KMS_CREDENTIALS': {
'aws': {
'key': '<AWS Key ARN>',
'region': '<AWS Key Region>',
},
},
},
}

Replace the following placeholder values:

  • <connection string>: The same connection string you specified in the default key

  • <AWS Access Key ID>: Your AWS access key ID

  • <AWS Secret Access Key>: Your AWS secret access key

  • <Automatic Encryption Shared Library path>: The full path to your Automatic Encryption Shared Library

  • <AWS Key ARN>: The Amazon Resource Name to the AWS customer.

  • <AWS Key Region>: Your AWS region, such as 'us-east-1'

DATABASES = {
'default': {
# ... Your default configuration
},
'encrypted': {
'ENGINE': 'django_mongodb_backend',
'HOST': '<connection string>',
'NAME': 'medical_records',
# ...
'OPTIONS': {
'auto_encryption_opts': AutoEncryptionOpts(
key_vault_namespace='medical_records.__keyVault',
kms_providers={
'azure': {
'tenantId': '<Azure Tenant ID>',
'clientId': '<Azure Client ID>',
'clientSecret': '<Azure Client Secret>',
},
},
crypt_shared_lib_path='<Automatic Encryption Shared Library path>',
crypt_shared_lib_required=True,
)
},
'KMS_CREDENTIALS': {
'azure': {
'keyVaultEndpoint': '<Azure Key Vault Endpoint>',
'keyName': '<Azure Key Name>',
# Optional: 'keyVersion': '<Azure Key Version>',
},
},
},
}

Replace the following placeholder values:

  • <connection string>: The same connection string you specified in the default key

  • <Azure Tenant ID>: Your Azure Active Directory tenant ID

  • <Azure Client ID>: Your Azure application client ID

  • <Azure Client Secret>: Your Azure application client secret

  • <Automatic Encryption Shared Library path>: The full path to your Automatic Encryption Shared Library

  • <Azure Key Vault Endpoint>: Your Azure Key Vault endpoint URL.

  • <Azure Key Name>: The name of your key in Azure Key Vault

  • <Azure Key Version>: (Optional) The version of the key to use

DATABASES = {
'default': {
# ... Your default configuration
},
'encrypted': {
'ENGINE': 'django_mongodb_backend',
'HOST': '<connection string>',
'NAME': 'medical_records',
# ...
'OPTIONS': {
'auto_encryption_opts': AutoEncryptionOpts(
key_vault_namespace='medical_records.__keyVault',
kms_providers={
'gcp': {
'email': '<GCP Service Account Email>',
'privateKey': '<GCP Service Account Private Key>',
},
},
crypt_shared_lib_path='<Automatic Encryption Shared Library path>',
crypt_shared_lib_required=True,
)
},
'KMS_CREDENTIALS': {
'gcp': {
'projectId': '<GCP Project ID>',
'location': '<GCP Key Ring Location>',
'keyRing': '<GCP Key Ring Name>',
'keyName': '<GCP Key Name>',
},
},
},
}

Replace the following placeholder values:

  • <connection string>: The same connection string you specified in the default key

  • <GCP Service Account Email>: Your Google Cloud service account email

  • <GCP Service Account Private Key>: Your Google Cloud service account private key

  • <Automatic Encryption Shared Library path>: The full path to your Automatic Encryption Shared Library

  • <GCP Project ID>: Your Google Cloud project ID

  • <GCP Key Ring Location>: The location of your key ring, such as 'global'

  • <GCP Key Ring Name>: The name of your key ring

  • <GCP Key Name>: The name of your key

DATABASES = {
'default': {
# ... Your default configuration
},
'encrypted': {
'ENGINE': 'django_mongodb_backend',
'HOST': '<connection string>',
'NAME': 'medical_records',
# ...
'OPTIONS': {
'auto_encryption_opts': AutoEncryptionOpts(
key_vault_namespace='medical_records.__keyVault',
kms_providers={
'kmip': {
'endpoint': '<KMIP Server Endpoint>',
},
},
crypt_shared_lib_path='<Automatic Encryption Shared Library path>',
crypt_shared_lib_required=True,
)
},
'KMS_CREDENTIALS': {
'kmip': {
# Optional: 'keyId': '<KMIP Key Identifier>',
},
},
},
}

Replace the following placeholder values:

  • <connection string>: The same connection string you specified in the default key.

  • <KMIP Server Endpoint>: Your KMIP server endpoint with its port, such as 'example.com:443'.

  • <Automatic Encryption Shared Library path>: The full path to your Automatic Encryption Shared Library.

  • <KMIP Key Identifier>: (Optional) The KMIP Unique Identifier to a 96-byte KMIP Secret Data managed object. If you omit this value, Django MongoDB Backend creates a random 96-byte object.

This code connects to the encrypted medical_records database, stores your data keys in the medical_records.__keyVault collection, and configures your KMS provider.

3

In your medical_records directory, create a file named routers.py and paste the following code into it:

class EncryptedRouter:
def allow_migrate(self, db, app_label, model_name=None, **hints):
# Create medical_records models only in the encrypted database.
if app_label == "medical_records":
return db == "encrypted"
# Don't create collections for other apps in the encrypted db.
if db == "encrypted":
return False
return None
def db_for_read(self, model, **hints):
# All reads and writes for medical_records models go to the encrypted db.
if model._meta.app_label == "medical_records":
return "encrypted"
return None
db_for_write = db_for_read

This file routes database operations on all models in your medical_records application to the encrypted medical_records database.

Then, add your custom router by navigating to your settings.py file and replacing the DATABASE_ROUTERS setting with the following code:

DATABASE_ROUTERS = [
'django_mongodb_backend.routers.MongoRouter',
'medical_records.routers.EncryptedRouter',
]
4

In your medical_records directory, navigate to the models.py file and paste the following code:

from django.db import models
from django_mongodb_backend.models import EmbeddedModel
from django_mongodb_backend.fields import (
EmbeddedModelField,
EncryptedCharField,
EncryptedEmbeddedModelField,
)
class Patient(models.Model):
patient_name = models.CharField(max_length=255)
patient_id = models.BigIntegerField()
patient_record = EmbeddedModelField("PatientRecord")
class Meta:
db_table = "patients"
def __str__(self):
return f"{self.patient_name} ({self.patient_id})"
class PatientRecord(EmbeddedModel):
ssn = EncryptedCharField(max_length=11, queries={"queryType": "equality"})
billing = EncryptedEmbeddedModelField("Billing")
bill_amount = models.DecimalField(max_digits=10, decimal_places=2)
class Billing(EmbeddedModel):
cc_type = models.CharField(max_length=50)
cc_number = models.CharField(max_length=20)

This code creates a Patient model, which corresponds to the patients collection in your medical_records database. It also creates two embedded models, PatientRecord and Billing, to represent nested fields in the collection.

The code configures the following encrypted fields:

  • patient_record.ssn: Encrypted and configured for equality queries

  • patient_record.billing.cc_type: Encrypted, but not queryable

  • patient_record.billing.cc_number: Encrypted, but not queryable

After configuring your application and database connection, follow the steps in this section to insert and query encrypted documents.

1

Run the following commands from your django_qe directory to create migrations for your models and apply them to the database:

python manage.py makemigrations medical_records
python manage.py migrate --database encrypted
2

Run the following command to enter the Python shell:

python manage.py shell

Then, run the following command in the shell to import the required models:

from medical_records.models import Patient, PatientRecord, Billing
3

From your shell, run the following commands to create a Patient instance and insert a corresponding encrypted document into the database:

billing = Billing(cc_type="Visa", cc_number="4111111111111111")
record = PatientRecord(ssn="987-65-4320", billing=billing, bill_amount=1500)
patient = Patient.objects.create(
name="John Doe",
patient_id=12345678,
patient_record=record
)

If you navigate to your medical_records.patients collection on MongoDB Atlas, you can see that the document's patient_record.ssn and patient_record.billing fields are encrypted.

4

Run the following command to perform an equality query on the encrypted patient_record.ssn field:

Patient.objects.get(patient_record__ssn="987-65-4320")

If successful, the command return the following patient data:

<Patient: John Doe (12345678)>

Congratulations on completing the Queryable Encryption tutorial! You now have a sample Django application that uses Queryable Encryption to automatically encrypt and decrypt document fields. Your application encrypts sensitive data on the server side and queries the data on the client side.

To learn more about Queryable Encryption and Django MongoDB Backend, visit the following resources:

Back

Transactions

On this page