• caglararli@hotmail.com
  • 05386281520

Encrypting links between registered users and their sensitive data

Çağlar Arlı      -    16 Views

Encrypting links between registered users and their sensitive data

I want to make it impractical to link the users to their sensitive data without their passwords – even with a full access to the database.

Furthermore, if a user has multiple pieces of sensitive data, I also want to avoid linking the different pieces together

Based on the comments and some searching, I have updated the question.

I found a few similar questions but none seem to give the details I'm looking for, or have slightly different prerequisites (f.ex. data sharing).

So, in addition to registering website users and managing them with standard CRUD operations, I'm storing some possibly sensitive pieces of data from the users into the database. In that respect, this question is similar, and the answers offer guidelines for me as well. Specifically, I should not "store anything in [my] database that could be used to obtain the encryption key without knowing the password."

The client side is composed of simple html/css/js pages and, at the moment, a desktop app is not an option. I analyze the data only in groups (based on variables in the data) so that individual data are of no interest. However, I want to keep it possible for the users to see their own data and, for example, to delete the data if wanted.

What I'm thinking is generating a key for each piece of data, encrypting the key–data_id pairs into the database and decrypting them each time an unencrypted key is needed, either for storing data or when a user wants to see their data:

import json
from cryptography.fernet import Fernet


def get_key(password, data_id):

    # Check that the given password is valid
    if not self.check_password(password):
        raise KeyError('The given password is not valid')
    
    # Always use string representation of the data_id since json allows only string keys
    data_id_str = str(data_id)

    # Create a Fernet object with the password
    f = Fernet(password)
    
    # Set the encoding for the bytes <-> string conversion    
    encoding = 'utf-8'
    
    # Decrypt and load into a dict the existing keys
    if self.encrypted_keys:
    
        # Ensure that the encrypted keys are in bytes for the Fernet
        bytes_encrypted_keys = bytes(self.encrypted_keys)

        # Decrypt the encrypted keys and transform the bytes object into a string
        keys_string = f.decrypt(bytes_encrypted_key).decode(encoding)
        
        # Load the string into a dict
        keys_dict = json.loads(keys_string)
    
    # Create an empty dict if no keys defined
    else:
        keys_dict = {}
    
    # Try to get a key for the data_id
    try:
        key = keys_dict[data_id_str]
    
    # The key not found
    except KeyError:
        
        # Generate a new a URL-safe 32-byte key and decode as a string into the keys_dict 
        key = keys_dict.setdefault(
            data_id_str,
            Fernet.generate_key().decode(encoding),
        )
        
        # Turn the updated keys_dict into a string
        updated_keys_string = json.dumps(keys_dict)

        # Encode the string into bytes for the Fernet
        bytes_keys = updated_keys_string.encode(encoding)

        # Encrypt the updated keys
        self.encrypted_keys = f.encrypt(bytes_keys)

        # Save the encrypted keys into the database
        self.encrypted_keys.save()
    
    # Return the decrypted key for the data_id
    return key

Does this seem like a reasonable process? Are there some obvious flaws I'm missing? Is this overkill? Are there some other things I should consider?

I'm aware that a weak spot in this is the strength of the password. I'll try to manage that with the standard strength checks.

I also understand that an access to the server gives the ability to intercept the process and compromise the keys. Of course, if there was a way to prevent this, without a desktop app, I'd be interested. Currently, I'm hoping, at least, to secure the database.

Thank you for the advice!