【问题标题】:PHP Hashed Master Passwords in Code代码中的 PHP 哈希主密码
【发布时间】:2014-07-28 04:55:52
【问题描述】:

我正在开发一个使用 LDAP 服务器进行身份验证的 PHP Web 应用程序。但是,万一 LDAP 服务器出现故障,或者出现其他问题而我仍然需要访问系统,我希望直接在代码中编写一个主密码。

这个系统的前一次迭代(我没有写)只是将密码存储为明文(!)。所以:

if ($username == "dan" && $password == "32fsss") {

这当然是极不安全的。

所以我想解决这个问题。我正在考虑对密码进行哈希处理,所以这样做

$hashedPassword = password_hash($password);
if ($username == "dan" && $hashedPassword == "HASHPASSWORDSTOREDHERE") {

所以我会硬编码主密码的 hash,而不是实际密码本身。这安全吗?我知道被盗的哈希是不好的,但它比被盗的明文密码更好。

当然,所有用户输入都会经过适当的清理。

或者:对于拥有主密码访问系统还有其他想法吗?

【问题讨论】:

  • 如果用户正在查看源以查找散列,那么他们可能只是更改源以让自己进入...但是,是的,与散列相比更安全一些,因为它不会给出您可能正在使用的实际密码,该密码可能会在其他地方使用
  • 如果有人有权查看 PHP 代码,则说明有问题。因此,他们看到未散列的密码是您最不关心的问题。如果需要,可以将密码存储在 www 目录之外的文件中。
  • 如果哈希可以让你访问,它就像密码一样好。

标签: php security hash passwords


【解决方案1】:

如果这纯粹是一个只读事件并且他们得到的只是散列版本,那应该不是问题。话虽如此,我更喜欢使用hash() 函数而不是password_hash(),但这只是我个人的喜好。

唯一的问题是读取 php 文件并获得散列密码的攻击者是否有办法反转它(即彩虹表)。在这种情况下,您的系统变得不安全。这就是我偏爱hash() 而不是password_hash() 的原因。与hash()。要生成散列形式,我可以说:

$HashedMasterPassword = hash('md5', $MasterPassword . "MyMD5Salt") . hash('sha256', $MasterPassword . "MyShaSalt");

然后在我上传的代码中,我可以获取上述代码的输出 ($HashedMasterPassword) 并将该字符串硬编码到我的代码中。这增加了两种不同的盐,两种不同的散列函数,它减少了彩虹表攻击的可能性已经很低,因为攻击者需要同时发生 md5 + 第一个盐和 sha256 + 第二个盐的冲突。这是,我敢说,不可能。当然,鸽子洞效应表明这是可能的,而且确实如此,但这几乎是不可能的,我不会为此烦恼。

话虽如此,如果你真的想的话,使用password_hash() 并存储该哈希值是可以的。我只是个人不喜欢它。

编辑:添加实现细节

根据 cmets,我相信对于如何实施这一点存在一些混淆。假设我的主密码是thisIsMyPassword(我知道超级机密和超级安全)。好吧,我们需要生成硬编码的散列形式。硬编码的散列形式可以通过以下方式找到:

$MasterPassword = "thisIsMyPassword";
$HashedMasterPassword = hash('md5', $MasterPassword . "MyMD5Salt") . hash('sha256', $MasterPassword . "MyShaSalt");
echo $HashedMasterPassword;

重要提示:您不会将其上传到服务器。这将仅用于生成哈希,然后应将其永久删除。

它的输出是:6dcfc45fba2fff44b7cfc8df7245a1b7804c88d63a82ac2ba33e0de32ab7f20c5afe2c1784e87f1bae8a4f91f1a84833

因此,在需要主密码才能覆盖的代码中,您应输入:

$MasterHash = '6dcfc45fba2fff44b7cfc8df7245a1b7804c88d63a82ac2ba33e0de32ab7f20c5afe2c1784e87f1bae8a4f91f1a84833';
$SubmittedMasterPass = $_POST['MasterPassword'];
$HashedVersionOfSubmittedMasterPass = hash('md5', $SubmittedMasterPass . "MyMD5Salt") . hash('sha256', $SubmittedMasterPass . "MyShaSalt");
if($MasterHash !== $HashedVersionOfSubmittedMasterPass){
  die('Override Failed. Reason: Wrong Password.');
}else{
  // User has successfully logged in using the master password.
}

再次根据来自 cmets 的信息进行澄清,这比仅使用 SHA256 更安全,因为必须找到一个暴力破解的主密码,当MyMD5Salt 附加到它的末尾时会产生6dcfc45fba2fff44b7cfc8df7245a1b7,它也当MyShaSalt 附加到它的末尾时,必须产生804c88d63a82ac2ba33e0de32ab7f20c5afe2c1784e87f1bae8a4f91f1a84833。这意味着碰撞必须满足两种不同的算法,这比单独满足 Sha256 算法要困难得多。

但是,正如 cmets 中所指出的,这确实需要多几毫秒的时间来处理;因此,如果减少执行时间是您的重中之重,那么可能建议仅单独使用 Sha256,而不是结合使用 MD5 和 Sha256。

【讨论】:

  • 哇,这真的很安全(你的连接有)。但是为什么要打扰md5呢? SHA 还不够安全吗?或者仅仅因为你可以,为了增加冗余?
  • Sha256 已经非常安全了,但是,嘿,如果再花几毫秒不是问题,那么我总是提倡增加一些安全性。为了获得额外的安全性,甚至可以使用 SHA512 代替 SHA256。执行脚本确实需要几毫秒的时间,但这通常还不错。我真正的工作实际上涉及很多(不是双关语)密码学和正确存储处理过的文件,而不是多次存储文件。因此,我们同时使用 SHA256 和 MD5,因为两者都非常快,如果同时使用两者,几乎不可能发生计划内或意外的碰撞。
  • @SpencerGrantDoak - 由于破解单个 SHA256 哈希的唯一合理方法是暴力破解,因此双重哈希不会提高安全性,它只是计算 2 个哈希而不是一个。实际上,有人可能会争辩说,您首先通过计算 MD5 来缩小 SHA256 的可能性。相反,计算单个哈希值的时间很重要,时间越长越难暴力破解。
  • 当您说“缩小可能性”时,我明白您在说什么,但我不同意您对此事的看法。虽然是的,但计算 MD5 的冲突比 SHA256 更容易,但实际上两者都需要暴力破解,因为 MD5 部分将使用盐,因此通过使用盐(假设盐非常独特),彩虹表中不会存在 MD5 部分用盐。我将在我的答案中添加一些额外的代码,以阐明如何实现。
  • @SpencerGrantDoak - 我看到你的代码可以防止使用冲突登录。但是为了找到 original 密码,攻击者现在可以仅使用 MD5 进行暴力破解。较弱的算法定义了哈希的安全性。 Rainbow-tables 只对一次破解多个哈希有用,而不是一个。
【解决方案2】:

可以讨论这样的“后门”是否可取,但如果您无法自行决定,有两种方法可以保护这样的硬编码密码。只存储哈希肯定比明文密码好得多,因为每个对代码有读取权限的人都可以在运行的系统上使用它。

第一种可能性是使用具有成本因子的慢速哈希算法,例如您在示例中提出的password_hash()。如果攻击者对代码具有读取权限,他将不得不暴力破解此密码,才能在实时系统上使用它。慢速哈希算法将阻止暴力破解,而 MD5 和其他函数的速度太快了 (8 Giga MD5 per second)。缺点是验证密码时应用程序会变慢。

第二种可能性是一个非常强的密码。选择足够长的随机组合,例如使用密码管理器生成 70 个字符并将其用作主密钥。即使是像 SHA256 这样的快速算法也超出了暴力破解的范围,而且您的密钥不受字典攻击,甚至不需要加盐。

【讨论】:

    【解决方案3】:

    如果用户有权访问源代码。他们可以改变它,所以这并不重要。

    【讨论】:

    • 我在考虑误读。如果有人有写权限,那么他们可以绕过整个身份验证。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    • 2018-09-10
    • 1970-01-01
    相关资源
    最近更新 更多