2010-07-12 13 views
10

Un'applicazione Web contiene dati sensibili degli utenti. Né l'operatore dell'applicazione web né il provider di hosting dovrebbero essere in grado di vedere questi dati. Pertanto ho voluto memorizzare questi dati nel DB crittografato con la password di accesso degli utenti.Strategia di codifica per la protezione dei dati sensibili

dataInDB = encrypt (rawData, user password) 

Con questa strategia non è però possibile implementare il caso d'uso abituale per il recupero della password: Dal momento che di solito solo il valore hash della password viene memorizzato dal web app, l'applicazione non può inviare il vecchio, password dimenticata per l'utente. E con l'assegnazione di una nuova password, i dati crittografati nel DB non sono più leggibili.

C'è qualche altra soluzione?

+1

+1: Ottima domanda. Che situazione terribile essere in ... –

risposta

7

Una possibile soluzione (non sono responsabili per qualsiasi distruzione):

Quando la cifratura dei dati sensibili, non utilizzare la password dell'utente come chiave. Piuttosto, deriva la chiave dalla password dell'utente (preferibilmente utilizzando un algoritmo standard come PBKDF2). Nel caso in cui l'utente dimentichi la propria password, è possibile conservare una copia di questa chiave derivata (crittografata utilizzando una chiave diversa derivata dalla risposta dell'utente). Se l'utente dimentica la propria password, può rispondere alla domanda di sicurezza. Solo la risposta corretta decodificherà la password originale chiave (non la password originale). Ciò ti offre l'opportunità di crittografare nuovamente le informazioni sensibili.

Mostrerò usando lo pseudo codice (Python-esque), ma prima diamo un'occhiata a una possibile tabella per gli utenti. Non farsi prendere in colonne appena ancora, diventeranno chiari presto ...

CREATE TABLE USERS 
(
    user_name    VARCHAR, 

    -- ... lots of other, useful columns ... 

    password_key_iterations NUMBER, 
    password_key_salt  BINARY, 
    password_key_iv   BINARY, 
    encrypted_password_key BINARY, 
    question    VARCHAR, 
    answer_key_iterations NUMBER, 
    answer_key_salt   BINARY 
) 

Quando arriva il momento di registrare un utente, devono fornire una domanda e risposta:

def register_user(user_name, password, question, answer): 
    user = User() 

    # The question is simply stored for later use 
    user.question = question 

    # The password secret key is derived from the user's password 
    user.password_key_iterations = generate_random_number(from=1000, to=2000) 
    user.password_key_salt = generate_random_salt() 
    password_key = derive_key(password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    # The answer secret key is derived from the answer to the user's security question 
    user.answer_key_iterations = generate_random_number(from=1000, to=2000) 
    user.answer_key_salt = generate_random_salt() 
    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The password secret key is encrypted using the key derived from the answer 
    user.password_key_iv = generate_random_iv() 
    user.encrypted_password_key = encrypt(password_key, key=answer_key, iv=user.password_key_iv) 

    database.insert_user(user) 

Nel caso in cui l'utente dimentichi la propria password, il sistema dovrà comunque chiedere all'utente di rispondere alla sua domanda di sicurezza. La loro password non può essere ripristinata, ma la chiave derivata dalla password può essere. Questo permette al sistema di ri-crittografare i dati sensibili utilizzando la password nuova:

def reset_password(user_name, answer, new_password): 
    user = database.rerieve_user(user_name) 

    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The answer key decrypts the old password key 
    old_password_key = decrypt(user.encrypted_password_key, key=answer_key, iv=user.password_key_iv) 

    # TODO: Decrypt sensitive data using the old password key 

    new_password_key = derive_key(new_password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    # TODO: Re-encrypt sensitive data using the new password key 

    user.encrypted_password_key = encrypt(new_password_key, key=user.answer_key, iv=user.password_key_iv) 

    database.update_user(user) 

Naturalmente, ci sono alcuni principi generali di crittografia non esplicitamente evidenziati qui (modalità di cifratura, ecc ...) che sono il responsabilità dell'implementatore di familiarizzare con.

Spero che questo aiuti un po '! :)

Aggiornamento per gentile concessione di commento di Eadwacer

Come Eadwacer commentato:

vorrei evitare che deriva la chiave direttamente dalla password (entropia limitata e la modifica della password sarà necessario ri-codifica di tutto il dati). Invece, creare una chiave casuale per ogni utente e utilizzare la password per crittografare la chiave. Dovresti anche criptare la chiave usando una chiave derivata dalle domande di sicurezza.

Ecco una versione modificata della mia soluzione prendendo il suo consiglio eccellente in considerazione:

CREATE TABLE USERS 
(
    user_name      VARCHAR, 

    -- ... lots of other, useful columns ... 

    password_key_iterations  NUMBER, 
    password_key_salt    BINARY, 
    password_encrypted_data_key BINARY, 
    password_encrypted_data_key_iv BINARY, 
    question      VARCHAR, 
    answer_key_iterations   NUMBER, 
    answer_key_salt    BINARY, 
    answer_encrypted_data_key  BINARY, 
    answer_encrypted_data_key_iv BINARY, 
) 

Si potrebbe quindi registrare l'utente come segue:

def register_user(user_name, password, question, answer): 
    user = User() 

    # The question is simply stored for later use 
    user.question = question 

    # The randomly-generated data key will ultimately encrypt our sensitive data 
    data_key = generate_random_key() 

    # The password key is derived from the password 
    user.password_key_iterations = generate_random_number(from=1000, to=2000) 
    user.password_key_salt = generate_random_salt() 
    password_key = derive_key(password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    # The answer key is derived from the answer 
    user.answer_key_iterations = generate_random_number(from=1000, to=2000) 
    user.answer_key_salt = generate_random_salt() 
    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The data key is encrypted using the password key 
    user.password_encrypted_data_key_iv = generate_random_iv() 
    user.password_encrypted_data_key = encrypt(data_key, key=password_key, iv=user.password_encrypted_data_key_iv) 

    # The data key is encrypted using the answer key 
    user.answer_encrypted_data_key_iv = generate_random_iv() 
    user.answer_encrypted_data_key = encrypt(data_key, key=answer_key, iv=user.answer_encrypted_data_key_iv) 

    database.insert_user(user) 

Ora, reimpostare la password di un utente assomiglia a questo:

def reset_password(user_name, answer, new_password): 
    user = database.rerieve_user(user_name) 

    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The answer key decrypts the data key 
    data_key = decrypt(user.answer_encrypted_data_key, key=answer_key, iv=user.answer_encrypted_data_key_iv) 

    # Instead of re-encrypting all the sensitive data, we simply re-encrypt the password key 
    new_password_key = derive_key(new_password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    user.password_encrypted_data_key = encrypt(data_key, key=new_password_key, iv=user.password_encrypted_data_key_iv) 

    database.update_user(user) 

Spero che la mia testa funzioni ancora chiaramente stasera ...

+0

Grazie Adam, questo è esattamente quello che sto cercando. Proverò a implementare questa soluzione entro i prossimi giorni in Java usando l'API Java Crypto e posterò qui il risultato. – Dominik

+0

Fino ad ora una cosa non è chiara: Per il normale caso d'uso dell'app web (login e vedere i dati dell'utente decodificati) l'utente non deve rispondere alla domanda di sicurezza. È giusto che la chiave segreta della password, utilizzata per crittografare i dati sensibili, debba essere derivata allo stesso modo della creazione iniziale di quella chiave. E per riprodurre la stessa chiave, anche le iterazioni e il sale sono memorizzati all'interno del Db. – Dominik

+0

@Dominik: sei corretto, ecco perché memorizziamo le iterazioni e salt nel database. –

Problemi correlati