我只想指出PHP 5.5包含一个 密码哈希API 它提供了一个包装器 crypt() 。此API显着简化了散列,验证和重新散列密码哈希的任务。作者也发布了一个 兼容包 (以简单的单个password.php文件的形式 require 使用),对于那些使用PHP 5.3.7及更高版本并希望立即使用它的人。
crypt()
require
它现在只支持BCRYPT,但它的目的是很容易扩展到包含其他密码散列技术,并且因为技术和成本存储为散列的一部分,对你喜欢的散列技术/成本的更改不会使当前散列无效,框架将自动化,在验证时使用正确的技术/成本。如果您没有明确定义自己的盐,它还会处理生成“安全”盐。
API公开了四个功能:
password_get_info()
password_hash()
password_needs_rehash()
password_verify()
目前,这些函数接受PASSWORD_BCRYPT和PASSWORD_DEFAULT密码常量,这些密码常量目前是同义词,不同之处在于,当支持更新,更强大的哈希算法时,PASSWORD_DEFAULT“可能会在较新的PHP版本中发生变化。”在登录时使用PASSWORD_DEFAULT和password_needs_rehash()(并在必要时进行重新散列)应该确保您的哈希值对蛮力攻击具有相当的弹性,几乎没有工作。
编辑:我刚刚意识到罗伯特K的回答中简要提到了这一点。我将在这里留下这个答案,因为我认为它提供了更多关于它如何工作的信息以及它为那些不了解安全性的人提供的易用性。
我在这里找到了关于此问题的完美主题: https://crackstation.net/hashing-security.htm ,我希望你能从中受益,这里的源代码也能防止基于时间的攻击。
<?php /* * Password hashing with PBKDF2. * Author: havoc AT defuse.ca * www: https://defuse.ca/php-pbkdf2.htm */ // These constants may be changed without breaking existing hashes. define("PBKDF2_HASH_ALGORITHM", "sha256"); define("PBKDF2_ITERATIONS", 1000); define("PBKDF2_SALT_BYTES", 24); define("PBKDF2_HASH_BYTES", 24); define("HASH_SECTIONS", 4); define("HASH_ALGORITHM_INDEX", 0); define("HASH_ITERATION_INDEX", 1); define("HASH_SALT_INDEX", 2); define("HASH_PBKDF2_INDEX", 3); function create_hash($password) { // format: algorithm:iterations:salt:hash $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM)); return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" . base64_encode(pbkdf2( PBKDF2_HASH_ALGORITHM, $password, $salt, PBKDF2_ITERATIONS, PBKDF2_HASH_BYTES, true )); } function validate_password($password, $good_hash) { $params = explode(":", $good_hash); if(count($params) < HASH_SECTIONS) return false; $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]); return slow_equals( $pbkdf2, pbkdf2( $params[HASH_ALGORITHM_INDEX], $password, $params[HASH_SALT_INDEX], (int)$params[HASH_ITERATION_INDEX], strlen($pbkdf2), true ) ); } // Compares two strings $a and $b in length-constant time. function slow_equals($a, $b) { $diff = strlen($a) ^ strlen($b); for($i = 0; $i < strlen($a) && $i < strlen($b); $i++) { $diff |= ord($a[$i]) ^ ord($b[$i]); } return $diff === 0; } /* * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt * $algorithm - The hash algorithm to use. Recommended: SHA256 * $password - The password. * $salt - A salt that is unique to the password. * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000. * $key_length - The length of the derived key in bytes. * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise. * Returns: A $key_length-byte key derived from the password and salt. * * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt * * This implementation of PBKDF2 was originally created by https://defuse.ca * With improvements by http://www.variations-of-shadow.com */ function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) { $algorithm = strtolower($algorithm); if(!in_array($algorithm, hash_algos(), true)) die('PBKDF2 ERROR: Invalid hash algorithm.'); if($count <= 0 || $key_length <= 0) die('PBKDF2 ERROR: Invalid parameters.'); $hash_length = strlen(hash($algorithm, "", true)); $block_count = ceil($key_length / $hash_length); $output = ""; for($i = 1; $i <= $block_count; $i++) { // $i encoded as 4 bytes, big endian. $last = $salt . pack("N", $i); // first iteration $last = $xorsum = hash_hmac($algorithm, $last, $password, true); // perform the other $count - 1 iterations for ($j = 1; $j < $count; $j++) { $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true)); } $output .= $xorsum; } if($raw_output) return substr($output, 0, $key_length); else return bin2hex(substr($output, 0, $key_length)); } ?>
最后,数学上的双重散列不会带来任何好处。然而,在实践中,它对于防止基于彩虹表的攻击是有用的。换句话说,它与使用salt进行散列相比没有什么好处,它可以在您的应用程序或服务器上花费更少的处理器时间。
我通常使用SHA1和salt与用户ID(或其他一些用户特定的信息),有时我还使用恒定的盐(所以我有2份盐)。
SHA1现在也被认为有些受到损害,但程度远远低于MD5。通过使用盐(任何盐),您将阻止使用通用盐 彩虹桌 攻击你的哈希(有些人甚至通过搜索哈希使用谷歌作为一种彩虹表成功)。可以想象,攻击者可以使用您的盐生成彩虹表,这就是为什么您应该包含用户特定的盐。这样,他们将不得不为系统中的每个记录生成彩虹表,而不仅仅是整个系统的一个!通过这种类型的腌制,即使是MD5也非常安全。
虽然问题已得到解答,但我只想重申,用于散列的盐应该是随机的,而不是像第一个答案中建议的那样的电子邮件地址。
有关的更多解释─ http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/
最近我讨论了密码哈希是否随机腌制 比可猜测或已知的盐更安全 盐。让我们看看:如果系统存储密码被泄露为 以及存储随机盐的系统,攻击者将会 有权访问哈希以及盐,所以盐是随机的还是 不,没关系。攻击者可以生成预先计算的 彩虹表破解哈希。这是有趣的部分 生成预先计算的表并不是那么简单。让我们举个例子 WPA安全模型。您的WPA密码实际上从未发送过 无线接入点。相反,它是用您的SSID( 网络名称 - 如Linksys,Dlink等)。一个非常好的解释如何 这项工作就在这里。为了从哈希中检索密码,你会 需要知道密码以及salt(网络名称)。教堂 Wifi已经预先计算了具有前1000个SSID和的散列表 大约100万个密码。所有表的大小约为40 GB。 正如您可以在他们的网站上阅读的那样,有人在3天内使用了15个FGPA阵列 生成这些表。假设受害者正在使用SSID “a387csf3”和密码为“123456”,会被那些人破解 表?没有! .. 这不可以。即使密码很弱,表也是如此 没有SSID a387csf3的哈希值。这就是拥有的美 随机盐。它将阻止在预先计算的情况下茁壮成长的破解者 表。它可以阻止坚定的黑客吗?可能不是。但是使用 随机盐确实提供了额外的防御层。我们在的时候 这个主题,让我们讨论存储随机的额外优势 在单独的系统上的盐。场景#1:存储密码哈希值 在系统X上,用于散列的盐值存储在系统Y上。 这些盐值是可猜测的或已知的(例如用户名)场景#2: 密码哈希值存储在系统X和用于的盐值上 散列存储在系统Y上。这些盐值是随机的。如果 系统X已经被破坏,你可以猜到,有一个巨大的 在单独的系统上使用随机盐的优势(场景#2)。 攻击者需要猜测附加值才能破解 哈希值。如果使用32位盐,则2 ^ 32 = 4,294,967,296(约4.2 猜测的每个密码都需要迭代。
我正在使用 Phpass 这是一个简单的单文件PHP类,几乎可以在每个PHP项目中轻松实现。也可以看看 H 。
默认情况下,它使用Phpass中实现的最强可用加密,即 bcrypt 并回退到MD5以下的其他加密,以向Wordpress等框架提供向后兼容性。
bcrypt
返回的哈希可以原样存储在数据库中。生成哈希的示例用法是:
$t_hasher = new PasswordHash(8, FALSE); $hash = $t_hasher->HashPassword($password);
要验证密码,可以使用:
$t_hasher = new PasswordHash(8, FALSE); $check = $t_hasher->CheckPassword($password, $hash);
谷歌称PHP可以使用SHA256。
你绝对应该使用盐。我建议使用随机字节(而不是限制自己的字符和数字)。通常情况下,选择的时间越长,越安全,越慢。我猜,64字节应该没问题。
的 免责声明 强> :这个答案写于2008年。 从那时起,PHP就给了我们 password_hash 和 password_verify 并且,自推出以来,它们是推荐的密码哈希&amp;检查方法。 答案的理论仍然是一个很好的阅读。
的 免责声明 强> :这个答案写于2008年。
从那时起,PHP就给了我们 password_hash 和 password_verify 并且,自推出以来,它们是推荐的密码哈希&amp;检查方法。
password_hash
password_verify
答案的理论仍然是一个很好的阅读。
\0
哈希密码背后的目标很简单:通过破坏数据库来防止恶意访问用户帐户。因此,密码散列的目标是通过花费太多时间或金钱来计算明文密码来阻止黑客或黑客。而时间/成本是你的武器库中最好的威慑力量。
您希望在用户帐户上使用良好,健壮的哈希的另一个原因是为您提供足够的时间来更改系统中的所有密码。如果您的数据库遭到入侵,您将需要足够的时间 最小 如果不更改数据库中的每个密码,请锁定系统。
Whitehat Security的首席技术官Jeremiah Grossman, 在他的博客上说 在最近的密码恢复后,需要暴力破解他的密码保护:
有趣的是,在实现这个噩梦的过程中,我学到了很多关于密码破解,存储和复杂性的知识。 我开始意识到为什么密码存储比密码复杂性更重要。如果您不知道密码的存储方式,那么您真正依赖的就是复杂性。 这可能是密码和加密专业人员的常识,但对于普通的InfoSec或Web安全专家,我非常怀疑。
(强调我的。)
熵 。 (并不是说我完全赞同兰德尔的观点。)
简而言之,熵是密码内的变化程度。当密码只是小写罗马字母时,那只是26字符。这没有太大的变化。字母数字密码更好,有36个字符。但允许使用符号的大写和小写大约是96个字符。这比信件要好得多。一个问题是,为了使我们的密码令人难忘,我们插入模式 - 这减少了熵。哎呀!
密码熵是 近似 容易。使用全范围的ascii字符(大约96个可键入的字符)产生每个字符6.6的熵,对于未来的安全性,密码的8个字符仍然太低(52.679位的熵)。但好消息是:更长的密码和带有unicode字符的密码,确实会增加密码的熵并使其更难破解。
关于密码熵的讨论还有较长时间 加密StackExchange 现场。良好的Google搜索也会带来很多结果。
我在与@popnoodles谈过的评论中指出了这一点 强制执行 X长度的密码策略,X多个字母,数字,符号等,实际上可以通过使密码方案更可预测来减少熵。我同意。 Randomess尽可能真正随机,始终是最安全但最难忘的解决方案。
据我所知,制作世界上最好的密码是Catch-22。它不是难忘的,太可预测的,太短的,太多的unicode字符(难以在Windows /移动设备上键入),太长时间等等。没有密码对我们的目的来说真的足够好,所以我们必须像他们一样保护它们在诺克斯堡。
Bcrypt和 scrypt 是目前的最佳做法。 Scrypt 将会比bcrypt及时更好,但它还没有被Linux / Unix或网络服务器采用为标准,并且尚未对其发布的算法进行深入评估。但是,该算法的未来确实看起来很有希望。如果您正在使用Ruby,那么有一个 scrypt gem 这将帮助你,Node.js现在有自己的 scrypt 包。您可以通过PHP在PHP中使用Scrypt Scrypt 扩展或 Libsodium 扩展(两者均在PECL中提供)。
我强烈建议您阅读该文档 隐窝功能 如果你想了解如何使用bcrypt,或找到自己 好 包装纸 或使用类似的东西 PHPASS 用于更传统的实施。我建议至少12轮bcrypt,如果不是15到18。
当我得知bcrypt只使用blowfish的密钥时间表,并使用可变成本机制时,我改变了主意使用bcrypt。后者允许您通过增加blowfish已经很昂贵的密钥安排来增加强制密码的成本。
我几乎无法想象这种情况了。 PHPASS 支持PHP 3.0.18到5.3,所以它几乎可以在所有可以想象的安装上使用 - 如果不这样的话,应该使用它 知道肯定 你的环境支持bcrypt。
但是假设您根本不能使用bcrypt或PHPASS。然后怎样呢?
尝试实现 PDKBF2 随着 最大数量轮次 您的环境/应用程序/用户感知可以容忍。我推荐的最低数字是2500发。另外,请务必使用 hash_hmac() 如果可以使操作更难以重现。
进入PHP 5.5是一个 完整的密码保护库 这消除了使用bcrypt工作的任何痛苦。虽然我们大多数人在大多数常见环境中都使用PHP 5.2和5.3,尤其是共享主机,但@ircmaxell已经构建了一个 兼容层 对于即将向后兼容PHP 5.3.7的API。
实际需要的计算能力 裂纹 哈希密码不存在。计算机“破解”密码的唯一方法是重新创建密码并模拟用于保护密码的散列算法。哈希的速度与其强制的能力线性相关。更糟糕的是,大多数哈希算法都可以轻松并行化,以便更快地执行。这就是像bcrypt和scrypt这样昂贵的计划如此重要的原因。
您无法预见所有威胁或攻击途径,因此您必须尽最大努力保护您的用户 的 在前面 强> 。如果你不这样做,那么你甚至可能会错过这样一个事实:你被攻击直到为时已晚...... 而且你要承担责任 。为了避免这种情况,首先要采取偏执行为。攻击您自己的软件(内部)并尝试窃取用户凭据,或修改其他用户的帐户或访问其数据。如果你不测试系统的安全性,那么除了你自己,你不能责怪任何人。
最后:我不是密码学家。无论我说的是我的意见,但我碰巧认为这是基于良好的常识......以及大量的阅读。请记住,尽可能偏执,让事情尽可能难以入侵,然后,如果您仍然担心,请联系白帽黑客或密码学家,看看他们对您的代码/系统的看法。
SHA1 盐应该足够了(当然,取决于你是否正在为某些东西编码 诺克斯堡 或者在可预见的将来,您的购物清单登录系统。如果SHA1不够好,请使用 SHA256 。
盐的想法是让哈希结果失去平衡,所以说。例如,已知空字符串的MD5哈希是 d41d8cd98f00b204e9800998ecf8427e 。所以,如果有足够好内存的人会看到那个哈希并知道它是空字符串的哈希值。但如果字符串是盐渍的(比如,用字符串“ MY_PERSONAL_SALT “),'空字符串'的哈希值(即” MY_PERSONAL_SALT “)成为 aeac2612626724592271634fb14d3ea6 因此回溯非显而易见。我想说的是,最好使用它 任何 盐,而不是。因此,了解并不是太重要 哪一个 盐要用。
d41d8cd98f00b204e9800998ecf8427e
MY_PERSONAL_SALT
aeac2612626724592271634fb14d3ea6
实际上有 做这件事的网站 - 你可以给它一个(md5)哈希,并且它会吐出一个生成该特定哈希的已知明文。如果您可以访问存储普通md5哈希值的数据库,那么为这样的服务输入管理员的哈希并登录将是微不足道的。但是,如果密码被腌制,这样的服务将成为无效的。
此外,双散列通常被认为是不好的方法,因为它减少了结果空间。所有流行的哈希都是固定长度的。因此,您只能拥有此固定长度的有限值,并且结果变得不那么多变。这个 可以 被视为另一种形式的腌制,但我不会推荐它。
好 在适合我们需要盐 盐必须是独一无二的 所以让它生成吧
/** * Generating string * @param $size * @return string */ function Uniwur_string($size){ $text = md5(uniqid(rand(), TRUE)); RETURN substr($text, 0, $size); }
我们也需要哈希 我正在使用sha512 它是最好的,它是在PHP中
/** * Hashing string * @param $string * @return string */ function hash($string){ return hash('sha512', $string); }
所以现在我们可以使用这个函数来生成安全密码
// generating unique password $password = Uniwur_string(20); // or you can add manual password // generating 32 character salt $salt = Uniwur_string(32); // now we can manipulate this informations // hashin salt for safe $hash_salt = hash($salt); // hashing password $hash_psw = hash($password.$hash_salt);
现在我们需要在数据库中保存$ hash_psw变量值和$ salt变量
并且为了授权我们将使用相同的步骤......
这是保护客户密码的最佳方式......
附:对于最后两个步骤,您可以使用自己的算法... 但请确保您将来可以生成此哈希密码 当你需要授权用户时......