Search code examples
djangodjango-storagepython-cryptography

django encrypt files before storage


So I want to encrypt files before storage in django and decrypt them upon retrieval.

I am using a custom storage class for the same and the cryptography module.

import hashlib
import os
import uuid

import django.core.files.storage as storage
from cryptography.fernet import Fernet
from django.conf import settings
from django.core.files import File


class DefaultStorage(storage.FileSystemStorage):
    def __init__(self):
        super(DefaultStorage, self).__init__()
        self.encryptor = Fernet(settings.ENCRYPTION_KEY)

    def _save(self, name, content):
        encrypted = self.encryptor.encrypt(content.file.read())
        content.file.write(encrypted)
        print(content.file.read() == encrypted)
        return super(DefaultStorage, self)._save(name, content)

    def _open(self, name, mode='rb'):
        encrypted = open(self.path(name), mode).read()
        return File(self.encryptor.decrypt(encrypted))

    def get_available_name(self, name, max_length=None):
        # we return a hash of the file given,
        # in case we miss out on uniqueness, django calls
        # the get_alternative_name method
        dir_name, file_name = os.path.split(name)
        file_root, file_ext = os.path.splitext(file_name)

        file_root = hashlib.md5(file_root.encode()).hexdigest()
        name = os.path.join(dir_name, file_root + file_ext)
        return super(DefaultStorage, self).get_available_name(name, max_length)

    def get_alternative_name(self, file_root, file_ext):
        # we insert a random uuid hex string into the given
        # file name before returning the same back
        return '%s%s%s' % (file_root, uuid.uuid4().hex, file_ext)

I am overwriting the _save and _open methods here, but the class doesn't work as expected.

Under the save method, I want to encrypt the contents of the file, but when I print this:

print(content.file.read() == encrypted)

It returns false. This means that the file isnt even being encrypted in the firs place. What am I doing wrong here? Same for _open method? Can someone please help me? thanks a lot!

EDIT

    def _save(self, name, content):
        initial = content.file
        encrypted = self.encryptor.encrypt(content.file.read())
        content.file.write(encrypted)
        # the temporary file is already stored in the path
        # content.file.name
        # we need to write to this directory
        print(content.file.seek(0) == encrypted)
        return super(DefaultStorage, self)._save(name, content)

this print statement also returns False

EDIT 2

When I removed the existing _open method, I found out that the file could still be retrieved (without decryption). This means that the file isn't even being encrypted. Any idea why? Can someone please help me?

The _save method:

def _save(self, name, content):
        encrypted = self.encryptor.encrypt(content.file.read())
        content.file.write(encrypted)
        return super(DefaultStorage, self)._save(name, content)

Solution

  • Usually, content.file.read() leaves the cursor at the end of the file.

    If you want to read the file again, you need to move the cursor to the start; otherwise, .read() will just return empty, because there is nothing after the end of the file.

    content.file.seek(0)                     # places cursor at the start
    print(content.file.read() == encrypted)  # reads file again
    

    This can be done using content.file.seek(0) (place cursor at position 0), or by closing and opening the file again.


    But careful, reading and writing the same file can be tricky and cause problems. Rather write to an aux file and replace the original file with the new file afterwards.