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 ...
+1: Ottima domanda. Che situazione terribile essere in ... –