应该执行密码比较的最佳做法是什么

时间:2012-06-15 14:55:19

标签: security authentication web

在对网站进行用户身份验证时,是否应在数据库或网站中进行哈希生成和比较?

我的论点是网站应该将用户提供的密码(可能由Web服务器加密)传递给数据库。然后数据库用salt重新加密它并比较哈希值。数据库响应Web服务器用户的凭据是否有效。这样,最小的数据库离开数据库,基本上是是或否,没有存储的凭证信息。缺点是,数据库必须做更多的工作。

另一个论点是工作应该在Web服务器上完成。这里,Web服务器将创建哈希并从数据库请求存储的哈希并进行比较。在这种情况下,需要将salt从数据库传递回Web服务器以创建哈希。但是,随着Web服务器数量的增加,共享工作。

我个人认为第二种方法是潜在的安全风险。如果Web服务器受到危害,可以从数据库中请求salt和哈希值并轻松破解。

执行上述操作的最佳做​​法是什么?我忽视/遗失了什么吗?

由于

4 个答案:

答案 0 :(得分:3)

关于如何安全地存储密码的文章非常好:

http://throwingfire.com/storing-passwords-securely/

答案 1 :(得分:3)

你忽略了盐的目的。

salt用于防止针对散列密码的字典攻击。如果您的密码是“花生”并且哈希值为12345,那么我可以为字典中的每个单词预先生成哈希列表(包括您的密码),并通过查找我预先生成的密码集来快速找到您的密码哈希值。这是LinkedIn最近发生的事情。如果密码被腌制,我必须在危害数据库之后为每个盐值预先生成一个字典,这将非常昂贵。

此外,适当的随机生成的盐可以防止攻击者知道你和我有相同的密码(没有盐,我们有相同的哈希)。

我的观点是盐并不是秘密。它们不是公共信息,但攻击者可以访问盐值+哈希值并不一定意味着密码已被泄露。

答案 2 :(得分:3)

我怀疑你遇到的第一个问题(而且它是一个很大的问题)是你的数据库没有密码哈希函数。当然,它可能有MD5()和SHA1(),但这些是加密哈希函数。它有bcrypt()或scrypt()还是PBKDF2()?

使用加密哈希函数而不是密码哈希函数意味着LinkedIn密码可以如此快速地破解。如果您不使用上述功能之一,那么如果您的哈希值被泄露,您将同样容易受到攻击。


继续回答您的问题,假设您的数据库 支持密码哈希算法(使用bcrypt只是因为我必须选择一个)。两种选择是:

在数据库中哈希:

$db->query("SELECT COUNT(*) FROM users WHERE username = '?' AND password = BCRYPT(?, (SELECT salt FROM user WHERE username = '?'))", $username, $password, $username);
if($row['count'] != 1)
{
  // Not authenticated.  Throw exception.
}

在这种情况下,将原始密码发送到数据库,并返回简单的是或否(1或0)。可以加密此数据库通信。哈希和salt永远不会保存在应用程序中。

在应用程序中哈希:

$db->query("SELECT username, salt, password FROM users WHERE username = '?', $username);
if(bcrypt($password, $row['salt']) != $row['password'])
{
  // Not authenticated.  Throw exception.
}

在这种情况下,哈希和salt从数据库中提取到应用程序中,并在那里完成原始密码的哈希和比较。与数据库的通信仍然可以加密。原始密码永远不会保存在数据库内存中。

为了提高效率,我们可以假设两种哈希算法都是用C语言(或某些编译语言)编写的,并且可能由OS提供,因此需要同时进行。应用程序散列选项通过线路接收更多数据,数据库散列选项发送更多数据并且具有更复杂的查询(实质上是两个查询,一个用于获取盐,一个用于实现比较)。可能无法按照我编写查询的方式使用索引,但可以重写查询。由于两种情况下数据的大小可能仍然是一个TCP数据包,因此速度差异可以忽略不计。由于子查询,我会称这个为应用程序哈希选项的胜利。

曝光。我认为原始密码比哈希和盐更敏感。因此,限制原始密码的曝光似乎是更安全的赌注,使应用程序散列为最佳实践。

答案 3 :(得分:0)

计算机安全的一个好的经验法则是,如果你不得不问,你不应该自己做。但是,如果您的担心是在Web服务器遭到破坏时泄露密码详细信息,那么一种方法是将身份验证移动到自己的系统上,并且不要让Web服务器访问密码数据库。