以明文形式传输密码是不好的,所以做任何事情都是好的第一步。如果你要努力,那么知道如何正确地去做是值得的。
虽然 MD5 不再是一种强大的散列算法,但在 MD5 和 SHA256(甚至 SHA512)之间进行选择不如你如何使用它重要。让我们忽略散列算法的细节,先看看如何使用它。
使用散列的想法是字符串的散列总是相同的并且是单向操作。通过捕获字符串,确定密码是不可能的(或实际的)。近年来,随着彩虹桌的大量使用,这种情况变得不真实。彩虹表包含所有可能的密码(直到给定长度)和它们的哈希值,以便攻击者可以使用反向查找来发现密码。对于 16 个字符以下的密码,所有哈希算法都可以使用彩虹表。
这个问题有一些常见的解决方案。一种是执行散列多次(大约 1,000 次)。客户端和服务器都必须知道并预先确定确切的次数,以便它们可以执行相同的操作。这具有使哈希生成昂贵的优点和缺点。攻击者在计算上变得更加难以暴力破解,但如果彩虹表扩展到足够大,它们仍然有用。
一个更好但不太常见的解决方案是在密码中添加一个已知的随机字符串(通常称为 Salt)以使其变长(可能是 64 个字符)。客户端和服务器必须事先知道此盐。这种解决方案既便宜又容易,即使盐泄漏了也无所谓。
密码散列还有另一个常见问题。如果恶意用户知道用户密码的哈希值,那么对于设计不佳的系统来说,这与知道密码本身一样好。假设我们有一个需要用户名和密码哈希的 RPC 函数。知道密码哈希的恶意用户可以提交它,即使不知道密码,也可以访问系统。这个已知的密码哈希将继续工作,直到用户更改他们的密码,这可能是几个月或几年。需要一种方法来限制密码散列有用的持续时间。这是通过使用动态盐来实现的。
然后,认证就变成了一个多步骤的过程。
- 客户端连接到服务器并提供某种客户端(或设备)标识符,例如 UUID。
- 然后,服务器为该客户端标识符生成一个动态盐。动态盐只在短时间内有效(通常是几分钟到几小时)。
- 服务器将动态盐值、过期时间和关联的客户端标识符存储在数据库表中以供将来使用。
- 服务器将动态盐及其到期时间返回给客户端。
- 客户端使用上述两种机制中的任何一种对密码进行哈希处理,连接动态盐,再次哈希,然后尝试使用用户名、客户端标识符和动态盐哈希进行身份验证。
- 服务器通过检查用户已知密码哈希值与提交的值来验证提交的凭据,尝试连接和哈希该客户端标识符的每个已知动态盐。如果找到匹配项,则接受身份验证。
这(大致)是 MySQL 使用的机制。它足够安全,可以在没有 SSL 的情况下安全使用,但我始终建议使用 SSL,以便保护有效负载的其余部分。
如果您使用这样的安全机制,那么使用 MD5 或 SHA 变体并不重要。也就是说,对于任何新开发来说,不使用 SHA256 是没有意义的,除非有充分的理由需要 MD5。