我想在我的登录系统中实现一个salt,但我对它的工作原理有点困惑。我不能理解它背后的逻辑。我知道md5是一种单向算法,我遇到的所有函数似乎都将所有东西都散列在一起。如果是这样的话,如何获得口令以进行比较?我最大的问题是,加盐的用户密码怎么会比哈希密码更安全呢?如果数据库被攻破,散列和盐都在数据库中。这难道不是一个黑客所需要的吗?
我还在这里找到了另一篇文章,另一位开发人员说:
“确保您的盐和算法与数据库分开存储”
我想把盐存储在数据库里。如果我这样做了,这真的是一个问题吗?
我正在寻找一些帮助,以了解这是如何工作的,以及最佳实践可能是什么。任何帮助都是非常感谢的。
编辑:我想感谢每个人的回复和想法。尽管我现在可能更加困惑,但这对我来说无疑是一次学习的经历。再次感谢你们。
发布于 2010-02-03 07:24:52
哈希函数总是为相同的输入字符串返回相同的值。假设我的用户(Alice)的密码是secret
。使用md5()
对secret
进行散列会导致以下散列
5ebe2294ecd0e0f08eab7690d2a6ee69
使用字典(常用单词和密码的列表)或提供该服务的各种站点之一,当攻击者(Mallory)在字典中看到5ebe2294ecd0e0f08eab7690d2a6ee69 = secret
时,他可以很容易地发现密码是秘密的。
在散列之前加盐的过程使得在不知道您的盐的情况下使用字典攻击变得更加困难。请考虑以下几点:
<?php
$salt = '@!#%$@#$@SADLkwod,sdaDwqksjaoidjwq@#@!';
$hash = md5($salt . 'secret');
得到的散列现在是b58ad809eece17322de5024d79299f8a
,但是Alice的密码仍然是secret
。现在,如果Mallory得到了加了盐的散列,那么她很有可能在字典中找不到答案。如果她这样做了,字典会给她错误的答案。
永远不要在数据库中存储静态盐。最好将其与您的应用程序的配置一起存储(顺便说一句,这些配置不应该从web上获得)。
如果要使用动态salt,则需要使用数据库。使用现有有效数据的非空列来构建您的盐(基于秘密加密密钥的用户名的blowfish加密字符串通常是加密安全的)。不要使用单独的盐柱。如果不能使用现有列,请将盐合并到与散列相同的列中。例如,128位盐使用前32个字符,160位散列使用后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注入进入您的数据库,至少他/她检索的散列将不会有用,因为他/她将无法访问您的应用程序配置。如果你的服务器扎根了,那么无论你做什么,它都差不多结束了。
注意:在md5()
上可能还有其他类型的攻击,这就是为什么您使用更安全的散列算法,例如sha1()
。或者,更好的方法是使用Portable PHP password hashing framework,它在设计时考虑到了安全性,并且向后兼容几乎所有的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';
}
发布于 2010-02-03 07:57:51
使用salt的目的是为了防止攻击者通过预先计算的rainbow tables来摊销跨站点暴力攻击的成本(或者更好的做法是,对每个用户使用不同的salt :一个站点的所有用户)。
使用普通散列,攻击者只需计算一次这样的表(一个非常耗时、昂贵的操作),然后使用它快速找到任何站点的密码。当一个站点使用一个固定的盐时,攻击者必须专门为该站点计算一个新的表。当一个网站为每个用户使用不同的盐时,攻击者就可以停止使用彩虹表了--他将不得不暴力破解每个单独的密码。
单独储存盐并不是获得这种优势所必需的。从理论上讲,它将更加安全,因为它将中和字典或短密码的弱点。在实践中,这不值得麻烦,因为在一天结束时,您需要访问某个地方的salts来检查密码。此外,试图将它们分开会导致更复杂的系统-并且系统越复杂,出现安全漏洞的机会就越多。
编辑:我的具体建议:
发布于 2010-02-03 07:22:14
忘记使用salts (部分是因为您提到的原因),改用bcrypt:
有关详细解释,请参阅:http://codahale.com/how-to-safely-store-a-password/
https://stackoverflow.com/questions/2188507
复制相似问题