我有一种情况,我必须存储密码,因为我正在构建一个系统连接到另一个系统。另一个系统只允许一个用户帐户,连接到它的唯一方法是通过密码。这里不适合使用散列。我必须以一种我可以检索到的方式存储密码。
现在,知道这不是一个完美的系统,我正试图限制损害,如果有人以某种方式访问数据库。由于这个数据库需要在不同的平台上使用,所以我决定使用MySQL自己的内置加密功能。这样,我就不需要担心为各种语言和系统寻找兼容的加密/解密算法实现。我可以在查询中使用MySQL的函数。
存储密码时,我将使用AES_ENCRYPT("password", "encryption key")
。然后我意识到我可能应该使用一些盐,这样如果他们能够获得一个密码,那么就很难获得其他密码。但是等等!重点是什么?如果他们能得到一个密码,他们一定有加密密钥,对吗?
此外,这是一个分组密码。在某些情况下,盐几乎是无用的。
/* Returns 8CBAB2A9260975FF965E5A7B02E213628CBAB2A9260975FF965E5A7B02E21362FBB5D173CBAFA44DC406B69D05A2072C */
SELECT HEX(AES_ENCRYPT("passwordpasswordpasswordpassword", "encryption key"));
/* Returns 8CBAB2A9260975FF965E5A7B02E213628CBAB2A9260975FF965E5A7B02E21362C49AF8D5B194770E64FEF88767206391 */
SELECT HEX(AES_ENCRYPT("passwordpasswordpasswordpassworda", "encryption key"));
我的问题
我认为在像我这样的情况下使用对称加密时没有理由使用盐是对的吗?
假设我必须以允许我检索原始值的方式存储密码,还有其他方法我应该考虑吗?(我知道我需要小心加密密钥存储的位置和方式,并且我需要保护我的MySQL日志。)
通常对于标准AES,您会提供nonce (IV),以避免您描述的问题。
大幅度提高加密数据质量的一种方法是为每个帐户使用不同的主密码,而不是改变IV。基本上这是一些与密码混合的数据。您可以通过多种方式实现这一点,最简单的是使用concat。
。
- 创建一个随机序列
- 存储nonce || HEX(AES_ENCRYPT(password_to_store, master_password || nonce)
- 通过提取nonce检索,然后使用master_password || nonce解密数据
下面是一个示例,具有唯一的nonce ' iej383u8fjeew '(每次加密时都需要生成一个新的nonce)
SELECT CONCAT('iej383u8fjeiw', ':', HEX(AES_ENCRYPT("password", CONCAT("master_password", "iej383u8fjeiw"))))
-> "iej383u8fjeiw:61224653D4DA33D57A42FE5E5E10DEA9"
SELECT AES_DECRYPT(UNHEX(SUBSTRING_INDEX('iej383u8fjeiw:61224653D4DA33D57A42FE5E5E10DEA9', ':', -1)), CONCAT('master_password', SUBSTRING_INDEX('iej383u8fjeiw:61224653D4DA33D57A42FE5E5E10DEA9', ':', 1)))
-> "password"
或带变量:
SELECT CONCAT(nonce, ':', HEX(AES_ENCRYPT(password_to_encrypt, CONCAT(master_password, nonce))))
-> encrypted password
SELECT AES_DECRYPT(UNHEX(SUBSTRING_INDEX(encrypted_password, ':', -1)), CONCAT(master_password, SUBSTRING_INDEX(encrypted_password, ':', 1)))
-> password_to_encrypt
也就是说,尽管比没有nonce的版本安全得多,但仍然存在许多弱点和攻击向量。例如,记录查询或嗅探mysql数据包将显示密码和主密码!