一个web应用程序包含用户的敏感数据,既不应该是web应用程序的操作者也不应该是托管提供商能够查看这些数据。因此,我希望将这些数据加密后通过用户的登录密码存储在数据库中。
dataInDB = encrypt (rawData, user password)
使用这种策略无法实现通常用于密码恢复的用例:由于 Web 应用程序通常仅存储密码的哈希值,因此应用程序无法将旧的、遗忘的密码发送给用户。而且通过分配新的、偶然的密码,数据库中的加密数据将不再可读。
还有其他解决方案吗?
一个web应用程序包含用户的敏感数据,既不应该是web应用程序的操作者也不应该是托管提供商能够查看这些数据。因此,我希望将这些数据加密后通过用户的登录密码存储在数据库中。
dataInDB = encrypt (rawData, user password)
使用这种策略无法实现通常用于密码恢复的用例:由于 Web 应用程序通常仅存储密码的哈希值,因此应用程序无法将旧的、遗忘的密码发送给用户。而且通过分配新的、偶然的密码,数据库中的加密数据将不再可读。
还有其他解决方案吗?
一种可能的解决方案(我不对任何破坏负责):
在加密敏感数据时,不要使用用户的密码作为密钥。相反,应该从用户的密码中派生密钥(最好使用标准算法如PBKDF2)。以防用户忘记他们的密码,您可以保留此派生密钥的副本(使用从用户的答案派生的另一个密钥进行加密)。如果用户忘记了他们的密码,他们可以回答他们的安全问题。只有正确的答案才能解密原始密码密钥(而不是原始密码)。这使您有机会重新加密敏感信息。
我将使用(类似Python的)伪代码进行演示,但首先让我们看一下可能的用户表。暂时不要被列所困扰,它们很快就会变得清晰...
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
)
当需要注册用户时,他们必须提供一个问题和答案:
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)
如果用户忘记了他们的密码,系统仍然需要要求用户回答他们的安全问题。他们的密码无法恢复,但可以从密码中派生出密钥。这使得系统可以使用新密码重新加密敏感信息:
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)
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,
)
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)
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)
希望今晚我的头脑依然清晰...
这段内容与IT技术无关。