目前据说MD5部分不安全。考虑到这一点,我想知道使用哪种机制来保护密码。
这个问题,Is “double hashing” a password less secure than just hashing it once? 表明多次散列可能是一个好主意,而How to implement password protection for individual files?建议使用salt。
我正在使用PHP。我想要一个安全快速的密码加密系统。散列密码一百万次可能更安全,但也更慢。如何在速度和安全性之间取得良好的平衡?另外,我希望结果具有恒定数量的字符。
另外,我应该在数据库中存储两个字段(例如,一个使用MD5,另一个使用SHA)?它会使它更安全或更不安全吗?
如果我不够清楚,我想知道使用哪种散列函数以及如何选择好的盐以便拥有安全,快速的密码保护机制。
相关问题并未完全涵盖我的问题:
What's the difference between SHA and MD5 in PHP
Simple Password Encryption
Secure methods of storing keys, passwords for asp.net
How would you implement salted passwords in Tomcat 5.5
答案 0 :(得分:943)
答案 1 :(得分:129)
更简短,更安全的答案 - 根本不编写自己的密码机制,使用久经考验的机制。
大多数程序员都没有专业知识来安全地编写加密相关代码而不会引入漏洞。
快速自检:什么是密码拉伸以及您应该使用多少次迭代?如果您不知道答案,则应使用password_hash()
,因为密码机制的关键功能现在是密码机制的一个关键特性,因为CPU速度更快,并且使用GPUs and FPGAs来破解密码{ {3}}(使用GPU)。
例如,您可以使用5台台式机中安装的25个GPU billions of guesses per second。这是强制性的,即枚举和检查每个8个字符的Windows密码,包括特殊字符,并且不是字典攻击。那是在2012年,从2018年开始,您可以使用更少的GPU,或者使用25个GPU更快地破解。
对于在普通CPU上运行且非常快的Windows密码,还有许多彩虹表攻击。所有这一切都是因为Windows 仍然 crack all 8-character Windows passwords in 6 hours其密码doesn't salt or stretch - 不会像微软那样犯同样的错误!
另见:
password_hash()
或phpass
是最佳选择。答案 2 :(得分:41)
我不会以两种不同的方式存储密码哈希,因为系统至少与使用中最弱的哈希算法一样弱。
答案 3 :(得分:34)
从PHP 5.5起,PHP具有简单,安全的哈希和验证密码功能,password_hash()和password_verify()
$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));
password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false
当使用password_hash()
时,它会生成一个随机盐并将其包含在输出的散列中(连同使用的成本和算法。)password_verify()
然后读取该散列并确定盐和加密使用的方法,并根据提供的明文密码进行验证。
提供PASSWORD_DEFAULT
指示PHP使用已安装的PHP版本的默认哈希算法。究竟是哪种算法意味着在未来版本中随时间变化,因此它始终是最强大的可用算法之一。
增加成本(默认为10)会使散列更难以暴力破解,但也意味着生成散列并验证密码对您的服务器的CPU来说将更有效。
请注意,即使默认的哈希算法可能会发生变化,旧的哈希值也会继续验证,因为所使用的算法存储在哈希值中,并且password_verify()
会对其进行检测。
答案 4 :(得分:31)
虽然问题已得到解答,但我只想重申,用于散列的盐应该是随机的,而不是像第一个答案中建议的电子邮件地址。
有更多解释,请致电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 猜测每个密码都需要迭代。
答案 5 :(得分:26)
我只想指出PHP 5.5包含一个password hashing API,它提供crypt()
的包装。此API显着简化了散列,验证和重新散列密码哈希的任务。作者还发布了一个compatibility pack(以单个password.php文件的形式,你只需要require
使用),对于那些使用PHP 5.3.7及更高版本的人来说,现在想要使用它
它目前仅支持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的回答中简要提到了这一点。我将在这里留下这个答案,因为我认为它提供了更多关于它如何工作的信息以及它为那些不了解安全性的人提供的易用性。答案 6 :(得分:18)
我正在使用Phpass这是一个简单的单文件PHP类,几乎可以在每个PHP项目中轻松实现。另请参阅The H。
默认情况下,它使用在Phpass中实现的最强可用加密,即bcrypt
,并回退到MD5之前的其他加密,以向Wordpress等框架提供向后兼容性。
返回的哈希可以按原样存储在数据库中。生成哈希的示例用法是:
$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);
要验证密码,可以使用:
$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);
答案 7 :(得分:13)
要记住的事情
关于PHP的密码加密已经说了很多,其中大部分都是非常好的建议,但在你开始使用PHP进行密码加密之前,请确保你已经实现了以下内容或准备好实现。
服务器强>
PORTS
如果您没有正确保护运行PHP和DB的服务器,那么无论您的加密有多好,您的所有努力都毫无价值。大多数服务器的功能相同,它们分配了端口,允许您通过ftp或shell远程访问它们。确保更改了您具有活动状态的远程连接的默认端口。通过不执行此操作,您实际上已使攻击者在访问系统时减少了一步。
USERNAME
对于世界上所有好的东西,请不要使用用户名admin,root或类似的东西。此外,如果您使用的是基于unix的系统,请不要使root帐户登录,只应该是sudo。
PASSWORD
您告诉您的用户制作好的密码以避免遭到黑客攻击,也可以这样做。当后门敞开时,经过所有锁定前门的努力有什么意义。
<强> DATABASE 强>
SERVER 的
理想情况下,您希望将DB和APPLICATION放在不同的服务器上。由于成本原因,这并不总是可行的,但它确实允许一些安全,因为攻击者必须通过两个步骤才能完全访问系统。
USER 的
始终让您的应用程序拥有自己的帐户来访问数据库,并且只为其提供所需的权限。
然后为您提供一个单独的用户帐户,该帐户不会存储在服务器上的任何位置,甚至不会存储在应用程序中。
总是不要做这个根或类似的东西。
PASSWORD
遵循与所有正确密码相同的指导原则。也不要在同一系统上的任何SERVER或DB帐户上重复使用相同的密码。
<强> PHP 强>
PASSWORD
永远不会在您的数据库中存储密码,而是存储哈希和唯一的盐,我将在稍后解释原因。
HASHING
单方式哈希!!!!!!!,永远不要以可以反转的方式对密码进行哈希处理,哈希应该是单向的,这意味着你不要反转它们并将它们与密码进行比较,你而是以相同的方式散列输入的密码并比较两个哈希值。这意味着即使攻击者可以访问数据库,他也不知道实际的密码是什么,只知道它产生的哈希值。这意味着在最糟糕的情况下为您的用户提供更高的安全性。
有许多好的散列函数(password_hash
,hash
等等)但是你需要选择一个好的算法来使散列有效。 (bcrypt和类似它的是不错的算法。)
当散列速度是关键时,对Brute Force攻击的抵抗力越强。
散列中最常见的错误之一是散列并非用户独有。这主要是因为盐不是唯一产生的。
盐析
密码应始终在哈希之前加盐。 Salting会在密码中添加一个随机字符串,因此类似的密码在数据库中看起来不一样。但是,如果盐不是每个用户独有的(即:你使用硬编码的盐),那么你的盐几乎没有价值。因为一旦攻击者找到一个密码盐,他就会为所有人提供盐。
当你创建一个盐时,确保它对于密码是唯一的,然后将完成的哈希和盐存储在你的数据库中。这样做是为了让攻击者在获得访问权限之前必须单独破解每个盐和哈希值。这意味着攻击者需要做更多的工作和时间。
用户创建密码
如果用户通过前端创建密码,则表示必须将密码发送到服务器。这会打开一个安全问题,因为这意味着将未加密的密码发送到服务器,如果攻击者能够监听并访问,那么PHP中的所有安全性都毫无价值。总是安全地传输数据,这是通过SSL完成的,但是即使SSL也没有完美无瑕(OpenSSL&#39; Heartbleed漏洞就是这样的一个例子)。
同时让用户创建一个安全的密码,它很简单,应该永远完成,用户最终会感激它。
最后,无论您采取的安全措施是否100%安全,保护技术越先进,攻击就越先进。但是,遵循这些步骤将使您的网站更安全,而且不太可能让攻击者去追求。
这是一个PHP类,可以轻松地为密码创建哈希和盐
答案 8 :(得分:12)
谷歌称PHP可以使用SHA256。
你绝对应该使用盐。我建议使用随机字节(而不是限制自己的字符和数字)。通常情况下,选择的时间越长,越安全,越慢。我猜是64字节应该没问题。
答案 9 :(得分:8)
我在这里找到了关于这个问题的完美主题:https://crackstation.net/hashing-security.htm,我希望你能从中受益,这里的源代码也能防止基于时间的攻击。</ p>
<?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));
}
?>
答案 10 :(得分:7)
最后,数学上的双重散列不会带来任何好处。然而,在实践中,它对于防止基于彩虹表的攻击是有用的。换句话说,它与使用salt进行散列相比没有什么好处,它可以在您的应用程序或服务器上花费更少的处理器时间。
答案 11 :(得分:6)
我通常使用SHA1和salt与用户ID(或其他一些用户特定的信息),有时我还使用恒定的盐(所以我有2份盐)。
SHA1现在也被认为有些受到损害,但程度远远低于MD5。通过使用盐(任何盐),你阻止使用通用rainbow table来攻击你的哈希(有些人甚至通过搜索哈希使用谷歌作为一种彩虹表获得了成功)。可以想象,攻击者可以使用您的盐生成彩虹表,这就是为什么您应该包含用户特定的盐。这样,他们将不得不为系统中的每个记录生成彩虹表,而不仅仅是整个系统的一个!通过这种类型的腌制,即使MD5也非常安全。
答案 12 :(得分:4)
SHA1和一个盐就足够了(当然,取决于您是为Fort Knox编写内容还是为您的购物清单编写登录系统)。如果SHA1不够好,请使用SHA256。
盐的想法是让哈希结果失去平衡,所以说。例如,已知空字符串的MD5哈希是d41d8cd98f00b204e9800998ecf8427e
。所以,如果有足够好内存的人会看到那个哈希并知道它是空字符串的哈希值。但是如果字符串被腌制(例如,使用字符串“MY_PERSONAL_SALT
”),则“空字符串”(即“MY_PERSONAL_SALT
”)的哈希变为aeac2612626724592271634fb14d3ea6
,因此不明显回溯。我想说的是,最好使用任何盐,而不是。因此,了解使用哪种盐并不是太重要。
实际上有websites that do just this - 你可以为它提供一个(md5)哈希,并且它会吐出一个生成该特定哈希的已知明文。如果您可以访问存储普通md5哈希值的数据库,那么为这样的服务输入管理员的哈希并登录将是微不足道的。但是,如果密码被腌制,这样的服务将成为无效的。
此外,双散列通常被认为是不好的方法,因为它会减少结果空间。所有流行的哈希都是固定长度的。因此,您只能拥有此固定长度的有限值,并且结果变得不那么多变。 可以被视为另一种形式的腌制,但我不推荐它。
答案 13 :(得分:-6)
确定 在适合我们需要盐 盐必须是独一无二的 所以让它生成
/**
* 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变量
并且对于授权,我们将使用相同的步骤......
这是保护客户密码的最佳方式......
P.S。对于最后两个步骤,您可以使用自己的算法... 但请确保您将来可以生成此哈希密码 当你需要授权用户......