4

显然,对用户名和密码进行哈希处理并将它们保存为 cookie 并使用经过清理的 cookie 数据登录对你们(或我的网站的安全)来说还不够好。

这种方法是否足够好?

注册程序:

$salt= date('U');
$username= hash('sha256', $salt. $_POST['username']);
$password= hash('sha256', $salt. $_POST['password']);
$token = hash('sha256', $salt. (rand(1,10000000000000000000000000000000000000000000)));

要手动登录,用户输入他们的用户名和密码,它通过哈希传递并匹配。

成功登录后,将为用户创建两个厨师。一个持有未散列的用户名,另一个持有散列令牌。

当用户访问时,如果设置了 cookie,站点会对用户名进行哈希处理,然后使用这两个值进行登录。

这种方法的优点是它避免了将用户的密码存储在他们计算机上的任何位置。有人可以破解令牌并访问他们的帐户,但至少我不会危及用户的密码......

回答MySQLreal_escape字符串和 cookie的人说,将用户数据存储在 cookie 中是错误的。这会转移问题吗?

4

4 回答 4

5

为什么您需要存储密码,甚至是加密版本?您的站点是否在后端访问执行 HTTP Basic auth 或其他内容的第 3 方 API?

不幸的是,您的问题没有明确的答案。“合适”对不同的人意味着不同的东西。对于安全问题,我不确定是否有可能“足够合适”或“足够安全”。也就是说,这就是我处理登录名和密码安全性的方式。在我的数据库users表中,我有 3 个与登录相关的列:

  1. 用户名
  2. 密码哈希

username列是纯文本格式。该salt列是由随机选择的字母数字字符组成的 64 个字符的字符串。该passwordHash列是用户的密码与其salt值连接,然后不可逆地散列。我使用 sha256 作为我的哈希值。盐是 64 个字符,因为这是 sha256 产生的。最好有一个至少与哈希值一样长的盐值,以便在哈希字符串中产生足够的可变性。

当用户提交登录表单时,我对用户名进行数据库查询。如果未找到用户名,我会向用户显示“无效的用户名和/或密码”错误。如果找到用户名,我将盐与密码连接起来,对其进行哈希处理,并查看它是否等于该passwordHash值。如果不是,则向用户显示完全相同的错误。

无论用户名是否错误,密码是否错误,或两者兼而有之,都最好显示完全相同的错误消息。你给黑客的线索越少越好。此外,每当用户更改密码时,我也会给他们一个新密码salt。在那个时候这样做真的很容易,而且它使盐值保持新鲜一点。

这种每个用户使用不同盐的系统称为动态加盐。如果黑客试图使用彩虹表对用户密码进行逆向工程,这会使黑客的工作变得非常复杂。更不用说以不可逆的散列形式存储密码对于防止任何人确定用户的密码有很长的路要走,即使他们有权访问数据库和 PHP 代码。

这也意味着如果您的用户忘记了密码,则无法找回密码。相反,您编写系统只是将其重置为一个新的随机确定的值,该值会发送给他们,并强烈鼓励他们在再次登录后立即更改密码。您甚至可以编写系统以在下次成功登录时强制执行此操作。

我要求密码至少为 8 个字符。理想情况下,它还应该包括数字和特殊字符,但我还没有决定我应该需要这个。也许我应该!

为了防止暴力攻击,我会跟踪过去 10 分钟内所有失败的登录。我根据每个 IP 地址跟踪它们。在 3 次登录尝试失败后,系统使用该sleep()功能延迟响应进一步的登录尝试。我使用这样的代码块:

$delay = ($failedAttempts - 3);
if ($delay > 0) {
    sleep($delay);
}

恕我直言,这比在多次失败后将用户锁定在他们的帐户之外要好得多。它减少了您将获得的客户支持查询的数量,并且对于根本不记得自己的密码的合法用户来说更加优雅。蛮力攻击需要每秒进行多次尝试才能获得任何形式的效率,因此n = x基于延迟可以防止它们走得太远。

使用 PHP 会话跟踪登录会话。session_start()加载您网站上的每个页面时调用。(如果您有一个公用文件,这真的很容易header.php。)这使得$_SESSION变量可用。当用户成功登录时,您可以使用它来存储站点所需的任何信息,以便知道用户已登录。我通常使用他们的用户 ID、用户名,也许还有一些特定于站点的其他详细信息。但我在这里没有包含密码或它的哈希值。如果黑客以某种方式进入了存储在您服务器上的用户会话数据,他们仍然没有机会通过这种方式找到用户的密码。

发生以下两种情况之一时会发生注销:1) 用户的会话 cookie 被删除,例如通过清除浏览器缓存或有时仅通过关闭浏览器窗口,或 2) 您的服务器删除他们的会话数据。session_destroy()当用户按下您网站上的“注销”按钮时,您可以通过调用强制后者发生。否则,您可以使会话在一段时间后自动过期。这可能涉及调整你的session.gc_*参数php.ini

如果您在初始登录阶段后绝对必须知道用户密码,您可以将其存储在$_SESSION. 当且仅当您的网站需要 SSL 连接并且您已经做到这一点时才这样做,这样该网站将无法在没有 SSL 连接的情况下运行。这样,密码就被加密了,因此可以防止数据包嗅探。但要知道,如果黑客获得了访问您服务器会话数据的权限,就会存在安全风险。

于 2012-07-03T02:32:27.770 回答
1

你的盐不够随机。

这个问题是重放攻击。假设坏人在访问您网站的途中收集了用户的 cookie。是什么阻止他(坏人)发送相同的值并像他们是真正的用户一样进入?

将用户数据存储在 cookie 中是不好的。您可能会在用户浏览器的 cookie 中存储一个随机的 64 位数字。在应用程序的身份验证数据库中,您记录了随机数和显着属性,例如用户名、cookie 首次有效的时间、过期时间,可能还有浏览器信息和 IP 地址信息。

发送 cookie 时,您确认它似乎来自与发送 cookie 时相同的浏览器。

我没有根据最佳实践检查这一点,或者再次“可靠”识别用户需要什么。但是随机数据没有任何意义,所以如果坏人抓住它也不是世界末日。捕获的 cookie 的有效性是有限的,如果您捕获适当的细节,坏人必须相当准确地模拟用户的环境。

于 2012-07-03T01:23:14.150 回答
0

为什么要对用户名进行哈希处理?这不是秘密,无论如何您都将需要未散列(或至少未加盐)的用户名来查找登录凭据。

另外,我认为您会想要使用加密安全的 RNG,例如openssl_random_pseudo_bytes() 代替rand(). 不过,除此之外,基本想法看起来还不错。

于 2012-07-03T01:32:59.250 回答
0

如果我是开放网络(如 wi-fi 热点)上的恶意用户,并且想要控制您的用户帐户之一,我可能会尝试以下方法。保护自己。

  • 知道(从你在 SO 上的帖子)你使用 date('U') 作为你的盐,我会尝试使用字典来破解密码,在每次测试之前附加已知的盐。
  • 如果您允许未加密的流量,我会嗅探流量并劫持令牌。希望您的网站能提供一种方法来更改我的电子邮件地址,然后通过电子邮件将密码发送给我。
  • 如果我可以获得文件访问权限,我会从文件中提取令牌并直接使用它。

您可能会考虑的另一种方法是序列化字段,加密该字符串,然后在您的 cookie 中存储一个 base64 编码的消息。此外,将一些大而杂乱的字符串附加到您现有的盐中。

于 2012-07-03T01:46:28.783 回答