Crypter les liens entre les utilisateurs enregistrés et leurs données sensibles

Nov 20 2020

Je veux qu'il soit impossible de relier les utilisateurs à leurs données sensibles sans leur mot de passe - même avec un accès complet à la base de données.

De plus, si un utilisateur possède plusieurs éléments de données sensibles, je souhaite également éviter de relier les différents éléments entre eux

Sur la base des commentaires et de quelques recherches, j'ai mis à jour la question.

Je trouve un peu semblables questions , mais aucun ne semble donner les détails que je suis à la recherche, ou qui ont des conditions préalables légèrement différentes (partage de données) p.ex.

Ainsi, en plus d'enregistrer les utilisateurs du site Web et de les gérer avec des opérations CRUD standard, je stocke dans la base de données des données potentiellement sensibles des utilisateurs. À cet égard, cette question est similaire et les réponses m'offrent également des lignes directrices. Plus précisément, je ne devrais «rien stocker dans [ma] base de données qui pourrait être utilisé pour obtenir la clé de chiffrement sans connaître le mot de passe».

Le côté client est composé de simples pages html / css / js et, pour le moment, une application de bureau n'est pas une option. J'analyse les données uniquement en groupes (sur la base de variables dans les données) afin que les données individuelles ne présentent aucun intérêt. Cependant, je souhaite que les utilisateurs puissent voir leurs propres données et, par exemple, supprimer les données si vous le souhaitez.

Ce que je pense, c'est générer une clé pour chaque élément de données, chiffrer les paires clé-data_id dans la base de données et les déchiffrer chaque fois qu'une clé non chiffrée est nécessaire, que ce soit pour stocker des données ou lorsqu'un utilisateur souhaite voir ses données:

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

Cela vous semble-t-il un processus raisonnable? Y a-t-il des défauts évidents qui me manquent? Est-ce exagéré? Y a-t-il d'autres choses que je devrais considérer?

Je suis conscient qu'un point faible est la force du mot de passe. Je vais essayer de gérer cela avec les contrôles de résistance standard.

Je comprends également qu'un accès au serveur donne la possibilité d'intercepter le processus et de compromettre les clés. Bien sûr, s'il y avait un moyen d'éviter cela, sans application de bureau, je serais intéressé. Actuellement, j'espère au moins sécuriser la base de données.

Merci pour le conseil!

Réponses

user10216038 Nov 22 2020 at 03:44

Si je comprends bien:

Oubliez le cryptage et le décryptage et la protection des clés. Rien de tout cela n'est nécessaire.

Utilisez un hachage de la même manière que les identités de mot de passe sont conservées.

Le hachage devient un identifiant unique des données sans révéler l'utilisateur.

Vous dérivez un hachage complexe approprié du mot de passe fourni par l'utilisateur. L'utilisateur peut ensuite fournir ce mot de passe pour que vous puissiez le hacher à nouveau pour obtenir l'identifiant correspondant à ses données.

Pas de chichi, pas de soucis, pas de mots de passe stockés.

--EDIT pour votre contrainte nouvellement ajoutée--

De plus, si un utilisateur possède plusieurs éléments de données sensibles, je souhaite également éviter de relier les différents éléments entre eux

Utilisez un Salt aléatoire pour produire un ID de hachage différent pour chaque objet blob de données. En l'absence d'autres contraintes, vous devrez calculer le hachage pour chaque sel du système pour trouver une correspondance. Cela peut être trivial pour des centaines, mais pour des valeurs beaucoup plus importantes, vous voudrez peut-être une contrainte supplémentaire.