我如何在我的login密码中实现salt?

我想在我的login系统中实现一个salt,但是对于这个工作如何工作有些困惑。 我无法理解它背后的逻辑。 我知道md5是一种单向algorithm,我所遇到的所有function似乎都把所有东西都混合在一起。 如果是这样,那么如何取回密码进行比较呢? 我最大的问题是,如何腌制密码的用户密码比安全? 如果一个数据库被破坏,那么散列和salt一起就在数据库中。 这不是黑客所需要的吗?

另外还有一位开发者说:

“确保你的盐和algorithm与数据库分开存储”

我想将salt存储在数据库中。 这真的是个问题吗?

我正在寻求一些帮助来理解这是如何工作的,以及最佳实践可能是什么。 任何帮助是极大的赞赏。


编辑:我想感谢大家的回应和想法。 即使我现在可能更困惑,对我来说,这确实是一个学习的经验。 再次感谢你们。

散列函数总是为相同的inputstring返回相同的值。 假设我的用户(Alice)拥有密码密码。 散列使​​用md5() secret导致下面的散列

 5ebe2294ecd0e0f08eab7690d2a6ee69 

使用字典(常用词和密码的列表)或为您提供服务的各种站点之一,攻击者(Mallory)可以很容易地发现密码是在他的字典中看到的密码是5ebe2294ecd0e0f08eab7690d2a6ee69 = secret

哈希之前的腌制过程使得在不知道盐的情况下使用字典攻击变得更加困难。 考虑以下:

 <?php $salt = '@!#%$@#$@SADLkwod,sdaDwqksjaoidjwq@#@!'; $hash = md5($salt . 'secret'); 

由此产生的散列现在是b58ad809eece17322de5024d79299f8a但是Alice的密码仍然是secret 。 现在,如果马洛里把手放在腌制的哈希上,她很可能在她的字典中找不到答案。 如果她这样做,字典会给她一个错误的答案。

切勿在数据库中存储静态盐。 最好将它与应用程序的configuration一起存储(顺便说一下,不应该从网上获得)。

如果你要使用dynamic盐,你将需要使用数据库。 使用现有有效数据的非空列来构build您的salt(基于密钥encryption密钥的blowfishencryption的用户名string通常是密码安全的)。 不要使用单独的盐列。 如果您不能使用现有的列,请将您的盐与哈希列在同一列。 例如,对于128位的salt使用前32个字符,然后对160位的hash使用最后的40个字符。 下面的函数会产生这样一个哈希值:

 function seeded_sha1($string, $seed_bits) { if(($seed_bits % 8) != 0) { throw new Exception('bits must be divisible by 8'); } $salt = ''; for($i = 0; $i < $seed_bits; $i+=8) { $salt .= pack('c', mt_rand()); } $hexsalt = unpack('h*hex', $salt); return $hexsalt['hex'] . sha1($salt . $string); } function compare_seeded_sha1($plain, $hash) { $sha1 = substr($hash, -40); $salt = pack('h*', substr($hash, 0, -40)); $plain_hash = sha1($salt . $plain); return ($plain_hash == $sha1); } 

如果攻击者使用SQL注入攻击你的数据库,至less他/她检索的哈希将不会有用,因为他/她将无法访问你的应用程序configuration。 如果你的服务器得到了根植,那么无论你做什么,它都是非常游戏的。

注意:md5()上还有其他types的攻击,这就是为什么你使用更安全的散列algorithm,例如sha1() 。 或者,更好的办法是使用便携式PHP密码哈希框架 ,该框架在devise时考虑到了安全性,并向后兼容几乎任何PHP版本。

 require('PasswordHash.php'); $pwdHasher = new PasswordHash(8, FALSE); // $hash is what you would store in your database $hash = $pwdHasher->HashPassword( $password ); // $hash would be the $hashed stored in your database for this user $checked = $pwdHasher->CheckPassword($password, $hash); if ($checked) { echo 'password correct'; } else { echo 'wrong credentials'; } 

盐的一个重点是防止攻击者通过预先计算的彩虹表缓冲跨站点的暴力攻击的成本(或者更好的是,当为每个用户使用不同的盐时:站点的所有用户)。

通过简单的散列,攻击者可以计算一次这样的表(一个非常长,昂贵的操作),然后使用它来快速find任何网站的密码。 当一个站点使用一个固定的盐时,攻击者必须为该站点专门计算一个新的表。 当一个网站为每个用户使用不同的盐时,攻击者可以停止使用彩虹桌 – 他必须分别强制每个密码。

分开储存盐不是获得这个好处的必要条件。 从理论上讲,它会更加安全,因为它可以消除字典或短密码的弱点。 在实践中,不值得打扰,因为在一天结束的时候,你需要访问盐的地方来检查密码。 另外,试图分离它们会导致更复杂的系统 – 而且系统越复杂,安全漏洞的机会就越多。

编辑:我的具体build议:

  • 为每个用户生成长伪随机盐并存储在数据库中
  • 使用基于bcrypt的哈希
  • 理想情况下,不要自己实现,而是使用现有的库

忘记使用盐(部分原因你提到),使用bcrypt来代替:

有关很好的解释,请参阅: http : //codahale.com/how-to-safely-store-a-password/

其他的答案是好的,所以我只会提一个没有人提到的小问题。 您不希望对每个密码使用相同的盐,因为如果两个人拥有相同的密码,他们将具有相同的哈希值。 这暴露了攻击者可以利用的信息。

您可以使用相同的盐为每个用户以及Juraj的好主意将密码与其他不变的数据库字段(对用户来说是唯一的)进行组合。 但要小心,因为这些信息与密码有关。 如果您要将用户名和密码散列在一起以保证唯一的散列,那么您将无法更改用户名,而无需创build新用户并要求他们设置新密码。

作为每个用户拥有一个独特的salt并将其与密码散列一起存储的示例,我将在典型的Linux系统上指出/ etc / shadow 。

 root@linux:/root# cat /etc/shadow | grep root root:$1$oL5TTZxL$RhfGUZSbFwQN6jnX5D.Ck/:12139:0:99999:7::: 

在这里, oL5TTZxL是盐,而RhfGUZSbFwQN6jnX5D.Ck/是散列。 在这种情况下,明文密码是root ,我系统使用的散列algorithm是基于MD5的BSD密码algorithm。 (比我更新的系统有更好的散列algorithm)

你没有得到密码进行比较。 您在尝试login时encryption密码,并将存储的值与新encryption的值进行比较。

正如你所提到的,散列algorithm只能单向运行(或者只有在足够强大的情况下:-D)

对于你的腌制问题,我build议用一个静态盐串和一些来自数据库的dynamic数据散列一个密码,这个数据库在创build后应该改变

这是存储密码的一种非常安全的方式,即使数据库被攻破,黑客/cookies仍然需要得到你的静态string散列,并且需要猜测你如何应用所有的腌制。

例如,假设您有一个包含这些列的users表:

 id username password created_at 

一旦填充后的列idcreated_at不应该改变..

所以当你哈希用户的密码,你可以做到这么简单:

 <?php $staticSalt = '!241@kadl;ap]['; $userPass = 'my new pass'; // assuming $user variable is already populated with DB data // we will generate new hash from columns and static salt: $genPass = sha1($user['id'] . $userPass . $user['created_at'] . $staticSalt); ?> 

我希望这个帮助:)欢呼声

散列密码是为了让这些密码与您的pipe理员保密。

1)在你的数据库中保持纯文本密码是可以的,除了你的密码可以被pipe理员用来访问其他系统。

2)您可以使用一个单一的全局salt,它与密码(通过预先或异或)组合在一起,然后散列用于存储在数据库中。 但是这对于一个恶意的pipe理员以及为这一个盐devise的彩虹表很容易。

3)每个用户可以有一个单独的盐:数据库将用于存储盐,以及从密码/盐组合中得到的散列。 这将防止彩虹攻击,但蛮力攻击仍然是可能的。

4)最后,通过使用速度有限的硬件哈希解决scheme,您可以保持哈希函数的秘密。

这和你所能做的一样好。 由于人性的原因,密码的域名有限,容易受到暴力攻击。 我们试图阻止pipe理员获取用户密码,然后在他们不应该访问的其他系统上使用它们。

其他一些说明:

a)您可以使用密码/盐组合的bcrypt来减缓攻击者的暴力攻击。 但是既然我们是假设pipe理者,他们可以耐心。

b)保持盐与密码哈希分开是不是一个有效的防守,我们毕竟假设pipe理员。

c)使用现有的数据作为盐是好一点,但我怀疑现有的数据有一个随机盐的熵。

抨击用户的密码可能比只是对密码进行哈希处理更安全,因为它可以防止预计算攻击。

例如,如果一个黑客可以访问你的数据库,并且密码不被腌制,那么他可以在他的哈希数据库中查找哈希值(参见http://en.wikipedia.org/wiki/Rainbow_table )来得到原始密码。