保护PHP密码的哈希和盐值

当前听说MD5部分不安全。 考虑到这一点,我想知道使用哪一种机制进行密码保护。 php

这个问题, “双重哈希”密码是否比仅哈希一次密码安全? 建议屡次散列多是一个好主意,而如何对单个文件实施密码保护? 建议使用盐。 html

我正在使用PHP。 我想要一个安全,快速的密码加密系统。 将密码哈希一百万次可能更安全,但也更慢。 如何在速度和安全性之间取得良好的平衡? 另外,我但愿结果具备恒定数量的字符。 前端

  1. 哈希机制必须在PHP中可用
  2. 必须安全
  3. 它可使用盐(在这种状况下,全部盐都同样好吗?是否有任何方法能够生成优质盐?)

另外,我是否应该在数据库中存储两个字段(例如,一个使用MD5,另外一个使用SHA)? 它会使它更安全或更不安全吗? git

若是我不够清楚,我想知道要使用哪一个哈希函数以及如何选择合适的盐,以便拥有安全,快速的密码保护机制。 github

还没有彻底涵盖个人问题的相关问题: 算法

PHP中的SHA和MD5有什么区别
简单密码加密
安全的asp.net密钥,密码存储方法
您将如何在Tomcat 5.5中实现盐腌密码 shell


#1楼

我正在使用Phpass ,这是一个简单的单文件PHP类,几乎能够在每一个PHP项目中轻松实现。 另请参见H。 数据库

默认状况下,它使用Phpass中实现的最强大的可用加密,该加密是bcrypt并回退到MD5等其余加密,以向与Wordpress之类的框架提供向后兼容性。 tomcat

返回的哈希能够原样存储在数据库中。 生成哈希的示例用法是: 安全

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

要验证密码,可使用:

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);

#2楼

我只想指出,PHP 5.5包含一个密码哈希API ,该APIcrypt()提供了包装器。 该API大大简化了哈希,验证和从新哈希密码哈希的任务。 做者还发布了一个兼容性包 (以您只require使用的单个password.php文件的形式),适用于使用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的答案中简要提到的。 我将在这里保留这个答案,由于我认为它为不了解安全性的人提供了更多有关其工做方式和易用性的信息。


#3楼

我在这里找到了关于此问题的完美话题: 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));
}
?>

#4楼

尽管已经回答了问题,但我只想重申,用于哈希处理的盐应该是随机的,而不是像第一个答案中所建议的电子邮件地址那样。

有关更多说明,请访问-http : //www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

最近,我讨论了用随机位加盐的密码散列是否比用可猜想或已知盐加盐的密码散列更安全。 让咱们看看:若是存储密码的系统以及存储随机盐的系统遭到破坏,则攻击者将能够访问哈希和盐,所以盐是不是随机的都没有关系。 攻击者能够生成预先计算的彩虹表来破解哈希。 有趣的部分来了-生成预先计算的表并非那么简单。 让咱们以WPA安全模型为例。 实际上,您的WPA密码永远不会发送到Wireless Access Point。 而是使用您的SSID(网络名称,如Linksys,Dlink等)进行哈希处理。 这是一个很好的解释。 为了从哈希中检索密码,您将须要知道密码以及盐(网络名称)。 Wifi教堂已经预先计算了哈希表,该哈希表具备前1000个SSID和大约一百万个密码。 全部表的大小约为40 GB。 在您的网站上能够看到,有人使用15个FGPA阵列3天来生成这些表。 假设受害者使用的SSID为“ a387csf3”,密码为“ 123456”,那么这些表会破解它吗? 没有! .. 这不能够。 即便密码很弱,表也没有针对SSID a387csf3的哈希。 这是无盐的美丽。 它将阻止在预计算表上蓬勃发展的饼干用户。 它能够阻止坚决的黑客吗? 可能不会。 可是使用随机盐确实能够提供额外的防护层。 当咱们处于这个主题上时,让咱们讨论将随机盐存储在单独系统上的其余优势。 方案1:密码哈希存储在系统X上,用于哈希的盐值存储在系统Y。这些盐值是可猜想的或已知的(例如,用户名)方案2:密码哈希存储在系统X上,而且盐值用于哈希存储在系统Y中。这些盐值是随机的。 如您所料,若是系统X受到破坏,则在单独的系统上使用随机盐具备巨大优点(方案2)。 攻击者将须要猜想附加值才能破解哈希。 若是使用32位盐,则每一个猜想的密码将须要2 ^ 32 = 4,294,967,296(约42亿)次迭代。


#5楼

谨记

关于PHP的密码加密,已经有不少说法,其中大多数是很是好的建议,可是在您甚至开始使用PHP进行密码加密的过程以前,请确保已实现或准备好实现如下内容。

服务器

港口

若是您没有正确保护运行PHP和DB的服务器的安全性,那么加密的效果如何,都将毫无用处。 大多数服务器以相对相同的方式运行,它们分配有端口,以使您能够经过ftp或shell远程访问它们。 确保更改了活动的远程链接的默认端口。 经过不这样作,您实际上使攻击者在访问系统时少了一步。

用户名

对于世界上全部好的东西,请勿使用用户名admin,root或相似名称。 另外,若是您使用的是基于UNIX的系统,请不要使root账户登陆名不可访问,它应该始终仅是sudo。

密码

您告诉用户输入正确的密码以免被黑,请执行相同的操做。 后门彻底打开时,进行全部锁定前门的努力的意义何在?

数据库

服务器

理想状况下,您要将数据库和应用程序放在单独的服务器上。 因为成本缘由,这并不是老是可能的,可是它确实能够保证必定的安全性,由于攻击者必须通过两个步骤才能彻底访问系统。

用户

始终让您的应用程序拥有其本身的账户来访问数据库,并仅为其提供所需的特权。

而后为您提供一个单独的用户账户,该账户不会存储在服务器上的任何位置,甚至不会存储在应用程序中。

像往常同样,不要使这个根或相似的东西。

密码

请遵循与全部正确密码相同的准则。 另外,请勿在同一系统上的任何SERVER或DB账户上重复使用相同的密码。

的PHP

密码

永远不要在数据库中存储密码,而要存储哈希和惟一的盐,稍后我将解释缘由。

散列

单向哈希!!!!!!!,永远不要以能够反转的方式哈希密码,哈希应该是一种方式,这意味着您不要反转并将它们与密码进行比较,而是哈希输入的密码以相同的方式比较两个哈希值。 这意味着,即便攻击者能够访问数据库,他也不知道实际的密码是什么,而只是密码所产生的哈希值。 这意味着在最坏的状况下为用户提供更高的安全性。

那里有不少很好的哈希函数( password_hashhash等),可是您须要选择一个好的算法才能使哈希有效。 (bcrypt及其相似的算法是不错的算法。)

当散列速度是关键时,速度越慢越能抵抗暴力攻击。

散列中最多见的错误之一是散列不是用户独有的。 这主要是由于不是惟一地产生盐。

密码在散列前应先加盐。 Salting为密码添加了一个随机字符串,所以类似的密码在数据库中不会显示相同的密码。 可是,若是盐不是每一个用户都惟一的盐(即:您使用硬编码盐),那么您几乎会使盐变得一文不值了。 由于一旦攻击者找出了一个密码盐,他就对全部密码都加盐。

建立盐时,请确保它与盐盐密码惟一,而后将完整的哈希和盐盐都存储在数据库中。 这样作是为了使攻击者必须先破解每种盐和哈希,而后才能得到访问权限。 对于攻击者来讲,这意味着更多的工做和时间。

用户建立密码

若是用户正在经过前端建立密码,则意味着必须将其发送到服务器。 这带来了一个安全问题,由于这意味着未加密的密码将发送到服务器,而且若是攻击者可以侦听和访问,则您在PHP中的全部安全性都毫无用处。 老是安全地传输数据,这是经过SSL来完成的,可是即便SSL并不是天衣无缝,也要感到厌倦(OpenSSL的Heartbleed缺陷就是一个例子)。

还要使用户建立一个安全的密码,这很简单,应该始终这样作,最终用户将不胜感激。

最后,不管您采起什么安全措施都是100%安全,防御技术越先进,攻击就变得越先进。 可是,按照如下步骤操做将使您的站点更安全,而攻击者则更不但愿这样作。

这是一个PHP类,可轻松为密码建立哈希和盐

http://git.io/mSJqpw

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息