User Tools

Site Tools


Custom Encrypted Model Field


Pre requisites

This code was written for Django 1.6 and Python 2.7.
It's using AES as encryption in CBC mode.

The goal

We need to create a custom Model field to be used to encrypt different information (as passwords, sensitive information, etc). We mention that we don't need only a hashing function, but more like two ways encryption: we need to be capable of encrypting, but in the same time decrypting information we put into the database.

The encryption should deal with UTF-8; it builds the encrypted string as:


The default prefix is '_', but you can specify it in the init method. This prefix is used to make a difference between encrypted and non-encrypted data, so make sure you choose an unique one (related to your encryption method).

The code

This code could go in a custom in your project.

The important mention here is that you store your secret key in, under the name FIELD_ENCRYPTION_KEY.

# -*- coding: utf-8 -*-
import types
import base64
from Crypto import Random
from Crypto.Cipher import AES
from django.db import models
from yourapp.settings import FIELD_ENCRYPTION_KEY
class EncryptedField(models.Field):
    __metaclass__ = models.SubfieldBase
    def __init__(self, *args, **kwargs):
        self.prefix = kwargs.pop('prefix', '_')
        super(EncryptedField, self).__init__(*args, **kwargs)
    def get_internal_type(self):
        return 'TextField'
    def to_python(self, value):
        if value is None or not isinstance(value, types.StringTypes):
            return value
        if self.is_encrypted(value):
            value = value[len(self.prefix):]    # cut prefix
            value = base64.b64decode(value)
            iv, encrypted = value[:AES.block_size], value[AES.block_size:]  # extract iv
            crypto =[:32], AES.MODE_CBC, iv)
            raw_decrypted = crypto.decrypt(encrypted)
            value = raw_decrypted.rstrip("\0").decode('unicode_escape')
        return value
    def get_db_prep_value(self, value, connection, prepared=False):
        if not prepared:
            iv =
            crypto =[:32], AES.MODE_CBC, iv)
            if isinstance(value, types.StringTypes):
                value = value.encode('unicode_escape')
                value = value.encode('ascii')
                value = str(value)
            tag_value = (value + (AES.block_size - len(value) % AES.block_size) * "\0")
            value = self.prefix + base64.b64encode(iv + crypto.encrypt(tag_value))
        return value
    def is_encrypted(self, value):
        """checks if a string is encrypted against a static predefined prefix"""
        if self.prefix and value.startswith(self.prefix):
            return True
            return False

How it's used

In your model, assuming that you have a module utils/models

from utils.models.fields import EncryptedField
class Example(models.Model):
    name = models.CharField(max_length=128, db_index=True, unique=True)
    password = EncryptedField()

python/custom_encrypted_field.txt · Last modified: 2014/08/17 10:16 by admin