在存储您需要验证(但不用作明文)的用户密码时,当前的技术状态是:
- 哈希密码
- 使用盐
- 使用慢散列函数 - bcrypt、scrypt 等。
这提供了对窃取密码数据库的攻击者的最佳保护。它不能解决网络钓鱼、恶意软件、密码重复使用等其他问题。
但是还有一个问题:慢散列函数可能允许拒绝服务攻击。单个请求会消耗大量 CPU,这使得并发请求数量相对较少的 DOS 成为可能,从而难以使用 IP 节流等防御措施。
鉴于 JavaScript 在浏览器中性能的提高,现在在浏览器中执行此操作可能是有意义的。我在这里假设该站点使用 SSL,因此 JavaScript 是安全交付的。如果您不使用 SSL,那么使用慢速散列函数不太可能成为您的优先事项。bcrypt至少有一个JavaScript 实现。但是以简单的方式使用它会引入两个问题:
- 客户端需要从服务器获取盐。这会在登录时引入延迟,除非小心,否则可能会显示特定用户帐户是否存在。
- 如果散列纯粹在客户端完成,那么存储散列的好处就失去了。窃取密码哈希的攻击者可以简单地使用哈希登录。
但是,我认为这两个问题都有可以接受的解决方案:
- salt 可以生成为 hash(server_salt + user_name) - 其中 server_salt 是一个随机数,对服务器来说是唯一的,公共的,对所有用户都是一样的。生成的哈希似乎具有盐所需的属性。
- 服务器应该对它接收到的散列执行一个单一的、快速的散列操作。例如:服务器存储 SHA-256(bcrypt(salt, password))。客户端发送 bcrypt(salt, password) 然后服务器应用 SHA-256 并检查哈希。这不允许攻击者进行快速离线暴力攻击。他们可以进行 SHA-256(密码)的快速暴力破解,因为密码的熵有限 - 2^50 或 2^60 左右。但是 128 位 bcrypt(salt, password) 具有熵或 2^128,因此他们不能轻易地暴力破解它。
那么,这是一种合理且安全的方法吗?
我知道“不要推出自己的加密货币”的一般建议。但是,在这种情况下,我试图解决现成的加密无法解决的问题。对于一些基本的可信度,约翰史蒂文(该领域公认的专家)已经从“简要”分析中得到了积极的结果。