Eine mögliche Lösung (ich bin nicht für die Zerstörung verantwortlich):
Wenn sensible Daten zu verschlüsseln, nicht das Kennwort des Benutzers verwendet als Schlüssel. Eher, leitet den Schlüssel aus dem Benutzerpasswort (vorzugsweise mit einem Standardalgorithmus wie PBKDF2). Für den Fall, dass der Benutzer sein Passwort vergisst, können Sie eine Kopie dieses abgeleiteten Schlüssels behalten (verschlüsselt mit einem anderen Schlüssel, der von der Antwort des Benutzers abgeleitet wurde). Wenn der Benutzer sein Passwort vergisst, kann er seine Sicherheitsfrage beantworten. Nur die richtige Antwort entschlüsselt das ursprüngliche Passwort Schlüssel (nicht das ursprüngliche Passwort). Dies bietet Ihnen die Möglichkeit, die vertraulichen Informationen erneut zu verschlüsseln.
Ich werde demonstrieren mit (Python-esque) Pseudocode, aber zuerst schauen wir uns eine mögliche Tabelle für die Benutzer. Lassen Sie sich nicht nur noch in den Spalten gefangen, werden sie bald klar geworden ...
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
)
Wenn es darum geht, einen Benutzer zu registrieren, müssen sie eine Frage zur Verfügung stellen und beantworten:
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)
Sollte der Benutzer sein Passwort vergessen, muss das System den Benutzer trotzdem bitten, seine Sicherheitsfrage zu beantworten. Ihr Passwort kann nicht wiederhergestellt werden, aber der Schlüssel kann aus dem Passwort abgeleitet werden. Dadurch kann das System auf die sensiblen Daten mit dem neuen Passwort erneut verschlüsseln:
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)
Natürlich gibt es einige allgemeine Verschlüsselungs Prinzipien hier nicht explizit hervorgehoben (Chiffre-Modi, etc ...), die das sind Verantwortung des Implementierers, sich damit vertraut zu machen.
Hoffe das hilft ein wenig! :)
-Update mit freundlicher Genehmigung von Eadwacer Kommentar
Wie Eadwacer kommentiert:
ich vermeiden würde den Schlüssel direkt aus dem Passwort (begrenzte Entropie abzuleiten und das Kennwort zu ändern benötigen alle die erneut verschlüsseln Daten). Erstellen Sie stattdessen einen zufälligen Schlüssel für jeden Benutzer und verwenden Sie das Kennwort, um den Schlüssel zu verschlüsseln. Sie würden den Schlüssel auch mit einem Schlüssel verschlüsseln, der von den Sicherheitsfragen abgeleitet wurde.
Hier ist eine modifizierte Version meiner Lösung seine exzellente Beratung berücksichtigt:
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,
)
Sie dann den Benutzer registrieren würde, wie folgt:
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)
nun das Kennwort eines Benutzers zurückzusetzen sieht so aus:
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)
Hoffentlich funktioniert mein Kopf heute Nacht immer noch klar ...
+1: Große Frage. Was für eine schreckliche Situation zu sein ... –