盐值:哈希或明文?

3

我希望在数据库中存储一个(随机)盐值,与密码一起存储。现在的问题是:

我应该以哈希形式还是明文形式存储它?是否有任何区别(更安全,更快速?)?创建一个随机字符串需要多少工作量?

示例代码:

    //Creating random salt
    $saltchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&()*+,-./:;<=>?@[]^_`{|}~";

    $salt = uniqid(rand(), true).str_shuffle($saltchars);
    // Or should the salt be hashed (md5 or sha512 for more security)
    // $salt = hash('sha512', uniqid(rand(), true).$staticsalt.str_shuffle($saltchars));
    //Create user (with salt & pepper)
$sqlquery = "INSERT INTO users (user, password, salt) VALUES('$id','".hash('sha512', $staticsalt.$accesskey.$salt)."','".$salt."');"; 
    $sqlresult = mysql_query($sqlquery); 

记录一下:登录脚本

    $sqlquery = "SELECT salt FROM users WHERE user='$id';"; 
    $sqlresult = mysql_query($sqlquery); 

    if (mysql_num_rows($sqlresult) == 1) { 

    $salt = (mysql_fetch_array($sqlresult));

//Check if the password is correct
    $sqlquery = "SELECT * FROM users WHERE user='$id' AND password='".hash('sha512', $staticsalt.$accesskey.$salt[0])."'";
    $sqlresult = mysql_query($sqlquery);
    unset($accesskey, $salt);

    //Check whether the query was successful or not
    if($sqlresult) {
    if(mysql_num_rows($sqlresult) == 1) 
        {echo 'Login successfull';}
        else {die('Error: wrong user ID/password');}
    }
   }

我知道有很多网站讨论盐的优缺点,但没有人回答是否应该加密盐,也没有人展示如何编写一个具有随机盐(保存在数据库中)的登录脚本。

所以作为一个PHP初学者,我不知道这段代码是否安全?或者是否有任何技巧可以使其更快或更简洁...谢谢!


2
如果盐是真正随机的,并且您将其加密存储,那么您打算如何解密它? - Jeff
由于没有加密的方法,登录脚本将使用散列盐值,再次进行哈希(现在是使用:$staticsalt.$password.$salt),并将其与存储的“密码”进行比较。 - Nyoman
4
如果你在验证密码时使用哈希盐,那么通过哈希它实际上没有什么效果。因为你使用的是与盐存储方式相同的方式,所以盐实际上是以明文形式存储的。 - Guffa
我所看到使用哈希盐的唯一优点就是它会更长,但由于它以明文形式存储,这并不是真正的优势(正如你或Alexander Gessler所说的那样)... - Nyoman
@Nyoman:那么你没有对盐进行哈希处理,因为盐就是哈希函数的输出。 - Jim
4个回答

5

没关系。您可以假设盐是公共信息,因为盐有助于防止彩虹表辅助字典攻击,即使攻击者知道盐,攻击也不会更容易。您应该问自己为什么认为在第一次加密盐是个好主意。

另外,您应该阅读这篇文章:


有趣的是,迟缓变成了一个特性。 - zneak
好的,如果bcrypt是“唯一未来可靠的哈希算法”,为什么PHP核心中没有简单的实现呢? - Nyoman
@Nyoman - 有PHP实现。上面的页面链接到其中一个。 - Wayne
攻击者不能为已知的盐创建彩虹表,然后我们会回到使用盐的第一阶段,是这样吗?(还是我理解错了什么?) - Edw590
1
@DADi590 是的,他们可以这样做,但这样做可能是禁止的。现代计算机足够快,可以在时间尺度上为某些哈希函数(和足够弱的密码参数)生成表格,因此典型的防御措施是使用一个故意缓慢的迭代哈希函数,以便在您的有生之年内无法生成表格。 - Wayne
我发现很难理解为什么盐可以作为明文存储在数据库中。如果攻击者控制了数据库并使用其中一个盐与其彩虹表结合进行彩虹攻击,那将会怎样?我猜,为每个盐创建一个全新的彩虹表会使这种方法变得非常低效。 - asn

4

由于在登录脚本中需要使用盐来计算密码哈希值,因此不能仅存储盐的哈希值,因为这将是一个不可逆的操作,即原始盐将会丢失。

因此,我假设您想知道从随机字符串中获取原始盐并对其进行哈希处理是否会产生更好的盐。在这种情况下,使用哈希函数与“哈希”无关,它只是一种生成更长、似乎更随机序列的方法。然而,这毫无意义,因为哈希盐仍然需要以明文形式存储在数据库中!


2
具体来说,使用散列函数处理随机字符串并不能使其更加随机。如果散列函数完美,你得到的随机性是恰好相同的;但如果散列函数不够强大,就会减少盐(salt)的随机性。因此,应该使用足够长且非常规生成器产生的随机数字符串来作为盐,避免使用rand()这种过于简单的数学函数。 - tdammers
@tdammers 好的,我把随机数生成器改成了 mt_rand() - 我知道它不是完美的,但至少它似乎能够提供更好的随机数... - Nyoman

2
没有人谈论盐被哈希的问题,因为盐不能被哈希。如果你不知道为什么,而且真的“不知道这段代码是否安全”,请使用别人的认证系统,而不是创建自己的系统,因为如果你不知道自己在做什么,很容易留下巨大的安全漏洞。
编辑:
哈希是单向的。你输入数据,你不会取出数据。这就是它的作用。如果你把盐放进去,你就无法取出来。如果你无法取出来,你就无法将其添加到用户提供的密码中。如果你不能这样做,你就不能将其用作盐。

2
我认为你的回答大多数都没有回答问题。如果我是你,我会把使用别人的身份验证系统的建议移到评论中,并解释你的陈述,而不是说这应该是显而易见的。 - zneak
我编辑了我的答案来解释。如果你知道什么是盐和哈希,那么这很明显。如果你不知道,为什么要尝试编写它们呢?至于使用别人的系统的建议——如果有人问我如何朝自己的脚开枪,我不会告诉他们把枪对准脚然后扣动扳机,我会告诉他们他们真的不想这样做。 - Jim
我仍然建议他们使用其他人的系统而不是让他们难堪,并且不要发布任何答案。请参考Alexander Gessler的答案,他更好地解释了盐的问题。 - zneak
不了解身份验证系统并没有什么可尴尬的 - 我已经说过它很容易出错。使用别人的系统不是评论,而是答案。没有其他负责任的答案可给。 - Jim
好的,如果使用别人的系统是答案,您介意展示一个好的身份验证系统的例子吗?(使用 PHP) - Nyoman
我现在不使用PHP,但你最好使用其中一个更知名的框架的身份验证系统。 - Jim

2

盐的目的是防止有人使用同一个彩虹表来破解所有密码。为每个密码创建一个新的彩虹表只会使方法过于低效。您不必隐藏盐,隐藏它并不能增加安全性。

由于盐不是加密密钥,并且不必隐藏,因此在创建它们时不必非常复杂。只需使用普通的随机生成器选择一些随机字符即可。

此外,您无法对盐进行哈希处理,因为这样就无法在某人想要登录时使用它来检查密码了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接