the_game
the_game

Reputation: 261

Encrypt DateField in django models?

I am having a requirement that i have to encrypt some of the fields in a table. I am using Django 1.3.1 and postgres 9.1. I am referring to this app django-fields :-

https://github.com/svetlyak40wt/django-fields/blob/master/src/django_fields/fields.py

Here is my code :-

patient_type = EncryptedCharField(max_length=80, choices=CHOICES.PATIENT_TYPES)
date_of_birth = EncryptedDateField(null=True, blank=True)
gender = EncryptedCharField(max_length=1024, choices=CHOICES.GENDERS, blank=True)
contact_phone = EncryptedCharField(max_length=1024, blank=True)
security_question = models.ForeignKey(SecurityQuestion, null=True, blank=True)
security_answer = EncryptedCharField(max_length=1024, null=True, blank=True)

It stores everything in encrypted form in the database except date_of_birth.It throws this error

Traceback: File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response 111. response = callback(request, *callback_args, **callback_kwargs) File "/home/user/slave/old_careprep/CPMS-Source/careprep/../careprep/utilities/decorators.py" in _dec 14. return view_func(request, *args, **kwargs) File "/home/user/slave/old_careprep/CPMS-Source/careprep/../careprep/visit/views.py" in setup 202. patient.save() File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py" in save 460. self.save_base(using=using, force_insert=force_insert, force_update=force_update) File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py" in save_base 553. result = manager._insert(values, return_id=update_pk, using=using) File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py" in _insert 195. return insert_query(self.model, values, **kwargs) File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py" in insert_query 1436. return query.get_compiler(using=using).execute_sql(return_id) File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py" in execute_sql 791. cursor = super(SQLInsertCompiler, self).execute_sql(None) File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py" in execute_sql 735. cursor.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py" in execute 34. return self.cursor.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/postgresql_psycopg2/base.py" in execute 44. return self.cursor.execute(query, args)

Exception Type: DatabaseError at /visit/setup/ Exception Value: invalid input syntax for type date: "$AES$55403376ed906e119b0f7779554fbb51" LINE 1: ...L, NULL, '$AES$0452edae035cc33c4084e7b0fb39edd7', '$AES$5540... ^ Help will be appreciated.

Thanks

Upvotes: 0

Views: 426

Answers (2)

Harshit Jain
Harshit Jain

Reputation: 970

Solution in short

Use custom encryption fields

Here's an example for EncryptedCharField and EncryptedDateField

class EncryptedCharField(CharField):
    def _get_cipher(self):
        return Fernet(SECRET_KEY)   

    def from_db_value(self, value, expression, connection):
        cipher = self._get_cipher()
        if value:
            return cipher.decrypt(value.encode()).decode()
        return value

    def get_prep_value(self, value):
        if value:
            cipher = self._get_cipher()
            return cipher.encrypt(value.encode()).decode()
        return value


class EncryptedDateField(DateField):
    def _get_cipher(self):
        return Fernet(SECRET_KEY)

    def to_python(self, value: Any) -> Any:
        cipher = self._get_cipher()
        if value:
            date = cipher.decrypt(value.encode()).decode()
            return datetime.datetime.strptime(date, "%Y-%m-%d").date()
        return value

    def from_db_value(self, value, expression, connection):
        return self.to_python(value)
        
    def get_prep_value(self, value):
        if value:
            cipher = self._get_cipher()
            return cipher.encrypt(str(value).encode()).decode()
        return value

    def get_internal_type(self):
        return "TextField"

Explaination -

Data in CharFields are expected to be strings, when you're using EncryptedCharField the encrypted data is being stored as string in database.

for eg. in field gender value (Male) will be stored in encrypted form gAAAAABktoZJ-fQdjJH4OgPs3riR2Fg6vqgUhduunXMW3pAYwJ47j1rhAUeXH5dTwT1ZlurteqTpUlm9p8oLJg34hPd5XTtubA==

when you fetch data for this particular value you get gAAAAABktoZJ-fQdjJH4OgPs3riR2Fg6vqgUhduunXMW3pAYwJ47j1rhAUeXH5dTwT1ZlurteqTpUlm9p8oLJg34hPd5XTtubA== and then you decrypt it.

in case of date_of_birth field, when you try to encrypt data it will break, because encryption algorithms works on bytes type.

for fields expecting strings (CharField or TextField) encode -> encrypt -> decode -> save.

for fields like DateTime, JSONField, BooleanField etc.

convert in string -> encode -> encrypt -> decode -> save.

Upvotes: 0

the_game
the_game

Reputation: 261

I realised that i was using DateField earlier if i convert DateField to CharField and then apply EncryptedCharField it solves my problem

Upvotes: 1

Related Questions