【问题标题】:Am I supposed to store hashes for passwords?我应该存储密码的哈希值吗?
【发布时间】:2011-03-03 13:28:21
【问题描述】:

用户系统和密码:我正在查看 MD5 资料,我想知道密码的正常/良好做法是什么。现在,我认为人们对密码进行超级加密并存储哈希值。如果是这样,密码检查如何工作?我只是让输入的密码再次经过加密过程,然后用存储的密码检查哈希,对吗?

这个问题可能与上述相矛盾,但我的盐应该是随机生成的值吗?如果有,什么时候有用?

编辑:除了密码,在用户系统中,还有什么应该加密的好习惯?他们是否加密用户名或其他任何内容?

第二次编辑:什么是单向哈希?我的意思是,从技术上讲,我不能对我的源代码进行逆向工程吗?也许这是一个不好的问题,因为我对单向哈希知之甚少。

【问题讨论】:

  • 确保您知道“加密”和“散列”之间的区别!
  • 您介意解释一下吗?
  • 加密涉及获取一个值并通过一组特定的进程运行它以生成另一个值,这样初始值(希望)对于没有正确解密的任何人都是未知的(这会将值改回) .另一方面,散列只是一种将长值或大值映射到小数组/表的方法。
  • @Raven Dreamer 谢谢
  • stackoverflow.com/questions/287517/… 看到那个问题,它有实际知识渊博的答案。

标签: php encryption passwords


【解决方案1】:

切勿以可逆方式存储密码,始终使用单向哈希。通过对输入的密码进行哈希处理并将两个哈希值相互检查来进行检查。

【讨论】:

    【解决方案2】:

    “对吗?”


    如果您需要二进制答案:

     1
    

    【讨论】:

    • 二进制答案还不错!
    【解决方案3】:

    您应该存储密码的哈希值而不是实际可读的字符串,还可以考虑使用“Salting”以提高安全性

    【讨论】:

      【解决方案4】:

      首先你创建一个盐。

      注意示例是用 PHP 编写的

      // Setup a salt, this isn't "random" but it doesn't really have to be
      $salt = sha1(microtime());
      

      然后给密码加盐

      // First we hash the password, then XOR it with the salt hashing the result
      $hash = sha1(sha1($password) ^ $salt);
      

      $hash$salt 存储在数据库中。

      当用户输入密码时,将其与哈希进行比较

      if(sha1(sha1($entered_password) ^ $salt) == $hash)
          // Correct password
      

      切勿以可逆格式存储密码。另外我建议不要使用 MD5 作为哈希。

      编辑:除了密码,在用户中 系统,还有什么要加密的 作为一个好习惯?他们是否加密 用户名还是其他?

      密码没有加密,它们是经过哈希处理的。将散列(非常简单化)想象为接受一个数字并将其乘以 10 的东西。假设我想对数字30 进行哈希处理。我会说30*10 并得到300 作为30 的“哈希”。请注意,在不知道哈希函数如何工作的情况下,您无法从 300 派生 30

      这是一个非常简单的“哈希”,如果您知道它总是乘以 1,那么您可以轻松地将其反转。 Now take a look at the SHA1 hash function。它要复杂得多。不能简单地逆转。

      你会发现除了密码散列之外很少有任何东西,并且没有任何东西是加密的。加密数据库的开销将是巨大的。

      假设您可以将类似的盐/哈希模式应用于用户名,但是您会遇到陷阱。如果您想在代码中的某处使用该用户名怎么办?如果您想检查以确保它对表是唯一的怎么办?

      第二次编辑:什么是单向哈希?一世 意思是,从技术上讲,我不能逆转 设计我的源代码?也许这是 一个不好的问题,因为我不知道 很多关于单向哈希的内容。

      见上文 (or click here)。单向哈希就是这样。一种方式映射。 A => B 仅此而已。 B !=> AA 不能是 B 之外的任何内容。

      有人提到了XOR 操作的性能。虽然我觉得性能在很大程度上可以忽略不计,但我进行了快速测试。

      function microtime_float()
      {
          list($usec, $sec) = explode(" ", microtime());
          return ((float)$usec + (float)$sec);
      }
      

      现在运行

      $start_time = $this->microtime_float();
      
      for($i = 0; $i < 100000; $i++)
      {
       $sha = sha1(sha1(microtime()) . sha1(microtime()));
      }
      
      $end_time = $this->microtime_float();
      
      echo "1000 in " . ($end_time-$start_time) . " for CAT\n";
      
      
      $start_time = $this->microtime_float();
      
      for($i = 0; $i < 100000; $i++)
      {
       $sha = sha1(sha1(microtime()) ^ sha1(microtime()));
      }
      
      $end_time = $this->microtime_float();
      
      echo "1000 in " . ($end_time-$start_time) . " for XOR\n";
      

      尽可能多地重复。 initial writeup 使用错误日志,我得到以下结果:

      1000 in 0.468002796173 XOR
      1000 in 0.465842008591 XOR
      1000 in 0.466115951538 XOR
      1000 in 0.498080968857 CAT
      1000 in 0.506876945496 CAT
      1000 in 0.500174045563 CAT
      

      【讨论】:

      • @incrediman:你称之为经验,我称之为默默无闻的安全。存储盐是没有问题的,因为它的目的是帮助抵御预先计算的哈希攻击。对于破解的数据库(攻击者获取哈希 + 盐),您依赖算法的安全性。
      • @incrediman:没错,但除非他们也可以访问代码,否则他们将不得不找出我是如何使用盐的。最重要的是,他们仍然需要访问密码——他们仍然没有。如果您使用主键,他们也可以访问它。
      • @Frank Farmer:可能没有实际区别。我只是使用了连接,因为 XOR 字符串的想法对我来说毫无意义。我的评论是对密码和盐进行两次散列,这是不必要的(除非您希望散列变慢,这有时很有用)。
      • -1 因为您对两次编辑的回答都不正确; cryptographic hashgeneral hash 不同(因此,对于我们的上下文,乘以 10 不是哈希)。加密哈希必须(除其他外)computationally one-way - 这与单向的 mathematical definition 不同。
      • @BlueRaja:请原谅我把加密散列的定义简化了。
      【解决方案5】:

      密码的标准做法是不要将原始密码存储在任何地方。 Unix 密码过去使用“crypt”进行加密,该密码会使用随机盐。 salt 本身存储在加密密码的前两个字符中。当用户输入自己的密码时,系统会使用加密密码的两个字符作为盐对输入的密码进行加密,如果加密结果与存储的加密密码相同,则为匹配。使用 MD5 密码也可以完成类似的操作。

      这就是为什么好的网站永远不会通过电子邮件向您发送密码,而是会将您的密码重置为一次性值 - 因为他们不知道您的密码。

      稍微扩展一下:MD5 散列是一种单向函数——如果你散列相同的值,你会得到相同的散列,但你不能获取散列并以某种方式将其转换为值。两个值产生相同散列的可能性很小但有限(初始字符串越大或散列越小,可能性越大),但他们选择散列算法是为了让人们选择两个字符串的机会因为密码会散列到几乎无限小的相同值。你可以把单向哈希想象成一个绞肉机——你可以看看从绞肉机出来的肉,看看它是牛、羊肉还是猪,但你不能通过另一种方式,取回一头牛。

      因此,没有人可以恢复您的密码,因为它从未存储在他们系统的任何地方,只是它的哈希值。

      【讨论】:

      • 您能否更详细地说明关于从不通过电子邮件向您发送密码的最后一部分?
      • @Doug:从不发送电子邮件意味着“从不发送电子邮件”。不要通过电子邮件发送密码和用户凭据。许多地方实际上是通过电子邮件发送密码,这是一件坏事。
      • @S.Lott:这实际上不是我要问的。我理解“从不通过电子邮件发送您的密码”部分,但是您将如何“将您的密码重置为一次性值”?
      • 好的,这就是我的想法。如果他们要求重置密码,那么我只需创建一个随机(带有@$!符号)值并将其传递给加密过程以存储散列,然后将随机值发送给他们以供他们登录和更改密码?
      • @Doug,这肯定行得通,尽管您可能应该在重置密码之前使用其他一些身份验证系统(如安全问题),以防有人劫持了他们的电子邮件。安全问题并不像银行认为的那样安全,但我们也没有更好的办法。
      【解决方案6】:

      这个主题有很多变化。

      如需了解更多信息,请阅读:http://en.wikipedia.org/wiki/Digest_access_authentication

      然后阅读:https://www.rfc-editor.org/rfc/rfc2617

      通常:您只存储摘要。永远不要密码。

      按照 RFC2617,您应该存储用户名、领域和密码的摘要。

      客户端(“代理”)获取用户名、密码、领域等,并创建一个摘要,并将其发送到您的服务器。

      您的服务器根据用户名查找它的摘要版本。

      如果他们的摘要 == 服务器中保存的摘要,则您同意密码(以及其他所有内容)。

      如果他们的摘要!= 服务器中保存的摘要,则您不同意密码(或其他内容)。这意味着他们没有得到正确的领域或用户名,或者他们没有得到正确的随机数,或者出现了其他问题。他们不能被信任。

      完整的 RFC2617 包含其他数据以计算其他内容的摘要和密码摘要,以确保客户端正在响应。

      【讨论】:

        【解决方案7】:

        您存储散列密码的原因是,如果有人设法获取了您的用户表中的数据,他们仍然无法登录。单向散列无法解密(甚至无法通过加密它的人!)因此很难将散列密码用于任何事情。

        由于您不可能解密数据库中的密码,因此您需要获取已输入的密码并重复相同的过程对其进行哈希处理,然后比较哈希值以找到匹配项。因此,您的盐不能完全随机,因为您最终会得到不同的结果。

        除此之外,您真的不希望以未加密的形式传输密码,这就是登录页面通常是 HTTPS 页面的原因。

        【讨论】:

        • 一种单向哈希可以很容易地破解——这就是为什么要加盐的原因。得益于hugh预计算字典,对已知哈希机制(如md5)的字典攻击速度很快。
        • @TomTom:虽然可以破解哈希,但这并不“容易”。这需要时间和资源,成功取决于密码的“强度”。
        • @Sohne:盐可以随意随意。只需将其存储在数据库中即可。
        • 因为你储存了盐。看我的例子。 第一次存储密码时会生成随机盐值。后续登录尝试使用存储的盐。
        • 完全正确。 Salt的想法是“密码”的哈希对于2个用户来说是不一样的,否则我可以运行一个简单的字典攻击,因为我可以预先计算哈希。
        【解决方案8】:

        第二次编辑:什么是单向哈希?我的意思是,从技术上讲,我不能对我的源代码进行逆向工程吗?也许这是一个不好的问题,因为我对单向哈希知之甚少。

        密码学中的“单向”意味着“难以反转”。简单地说,就是如果我给你sha1(password),你在任何合理的时间内都找不到password

        这称为computationally one-way。许多人将此与单向(来自数学)的另一种定义混淆,即"not one-to-one",此处不适用。

        【讨论】:

          【解决方案9】:

          我强烈建议不要使用 MD5。如需更多信息,请阅读Wikipedia section [MD5] Security。我建议改用 SHA-1 哈希算法。

          【讨论】:

            【解决方案10】:

            尽可能避免使用 MD5 哈希,因为它存在很大缺陷。

            替代方案是 SHA1,甚至更好的 SHA256 或 SHA512。

            【讨论】:

              【解决方案11】:

              这个问题可能与 上面,但我的盐应该是 随机生成的值?如果是的话,什么时候 有用吗?

              盐应该是随机的。它们的唯一用途是使对哈希的暴力攻击更加昂贵。一种叫做“彩虹表”的东西(这是一个数据库的一个花哨的名称,其中有人提前散列了一大堆可能的密码,如果你知道散列,让你查找密码)可以获取未加盐的密码散列并转在许多情况下,它们会在几分之一秒内变成密码。

              中等大小的盐可以成倍地增加预先计算的蛮力攻击的复杂性。对于 salt 中的每一位随机数据​​,您将预先计算的蛮力攻击所需的时间加倍。对于数据库中的每个唯一盐值,攻击者在攻击受该盐保护的密码时必须重新开始。

              如果每个用户的密码都有 1kB 的随机盐,那么预先计算的哈希值就不会存在了。不过,您不会影响暴力破解单个用户密码所需的时间。

              使蛮力攻击者的生活更加艰难的一种方法是使散列过程计算密集(例如 5000 轮 sha1(salt+sha1(salt+sha1(salt+password))))。您只需为每次登录尝试执行此操作。攻击者必须对他们想要猜测的每个盐 + 密码组合进行此操作。您必须决定这是否值得满足您的需求。答案可能是否定的。

              编辑:除了密码,在用户中 系统,还有什么要加密的 作为一个好习惯?他们是否加密 用户名还是其他?

              我很偏执,但我想说,在用户未登录时网站所有者不需要的任何信息都应使用用户密码的派生密码进行加密。这样攻击者就没有访问权限,因为您没有访问权限。

              例如,对于在线订单处理系统,您可能需要他们的邮寄地址、他们的姓名和最近的订单未加密,但他们的订单历史记录和最喜欢的颜色可能会使用他们的帐户密码加密。

              请注意,如果您这样做,他们丢失了密码,受保护的信息也会丢失。

              第二次编辑:什么是单向哈希?一世 意思是,从技术上讲,我不能逆转 设计我的源代码?也许这是 一个不好的问题,因为我不知道 很多关于单向哈希的内容。

              散列是一种系统地丢弃信息的方法。假设您从一个字符串开始,并通过丢弃除大约每四个字符之外的所有字符来生成“srflcdos”。我“散列”的文本可能是:“如果鱼平静地躺着,就用矛。不要坐着!”,也可能是:“supercalifragilisticexpialidotious”。没有办法证明任何一种方式。

              加密哈希在丢弃的同时还进行了更多的混合和其他转换,以使它们对于少量输入数据更加安全,并避免泄露有关输入数据的任何事实。作为不安全散列的示例,如果您知道只要输入包含字母 A,散列的 12 位就是 1,那么您就是在暴露有关原始文本的信息,结果不是加密安全散列。

              原则是,如果在每次转换之间丢弃对反转先前转换至关重要的信息,则无法对流程进行逆向工程。无论您输入 1 位还是 12 PB 的信息,MD5sum 都会产生 128 位的输出。您显然无法将 12 PB 压缩为 128 位,因此在计算哈希的过程中显然会丢弃信息。

              【讨论】:

                猜你喜欢
                • 2012-08-19
                • 2015-10-07
                • 2011-10-26
                • 2018-02-13
                • 2011-04-29
                • 1970-01-01
                • 1970-01-01
                • 2014-01-17
                • 2011-07-17
                相关资源
                最近更新 更多