编辑:
让我推荐你看这个answer on Security StackExchange,它解释了很多关于密码散列和密钥派生的细节。
底线:使用安全的既定密码散列方案,该方案在某种程度上是资源密集型的,以防止暴力攻击,但限制允许调用的数量以防止拒绝服务 (DoS ) 攻击。
如果您的语言库有它的功能,请在升级时验证它是否能完成它应该做的事情,特别是如果它是 PHP。
下面的答案是历史原因留下的。
您可以使用用户的登录名作为盐,它可能比电子邮件地址更不可能更改(编辑: 0xA3 正确指出,这比使用电子邮件地址,因为登录名往往更容易猜到,并且有些非常常用,因此彩虹表可能已经存在,或者可以用于其他站点)。
或者,有一个数据库列,您可以在其中保存密码的盐。
但是,您也可以使用随机的用户特定盐,这很难猜到。
为了更好的安全性,您可以使用两种盐:一种特定于用户的盐和一种系统范围的盐(连接它们,然后使用密码对盐进行散列)。
顺便说一句,简单的盐和密码连接可能不如使用HMAC 安全。在 PHP 5 中,您可以使用 hash_hmac() 函数:
$salt = $systemSalt.$userSalt;
hash_hmac('sha1', $password, $salt);
编辑:系统范围盐的基本原理:它可以而且应该存储在数据库之外(但备份它。您将无法进行身份验证你的用户,如果你失去它)。如果攻击者以某种方式读取了您的数据库记录,在他知道系统范围的盐之前,他仍然无法有效地破解您的密码哈希。
编辑(稍微偏离主题):
关于密码散列安全性的进一步说明:您可能还想阅读Why do salts make dictionary attacks 'impossible'? 多次散列以额外保护免受暴力破解和彩虹表攻击(尽管我认为重复散列可能会带来额外的拒绝 -服务攻击,除非您限制每次登录尝试的次数)。
注意
考虑到多用途多核系统(显卡、可编程微控制器等)的兴起,可能值得使用具有高计算量的算法以及盐来对抗暴力破解,例如使用多个散列,如 PBKDF2。但是,您应该限制每个时间单位的身份验证尝试次数,以防止 DDoS 攻击。
还有一件事:使用基于广泛使用的标准而不是广泛使用的预构建函数的“自定义”散列的另一个主要理由是 PHP 本身,它已被证明不是在实现与安全相关的东西时完全值得信赖,无论是非随机随机数生成器还是特定情况下的crypt() function that does not work at all,从而完全绕过任何计算或内存密集型密码散列函数应该带。
由于它们的确定性结果,简单的哈希函数比密钥派生函数的输出更有可能被正确测试,但您的里程可能会有所不同。