草庐IT

php - 这在PHP中是一个很好的哈希密码功能吗?如果没有,为什么不呢?

coder 2023-06-13 原文

我想知道此功能(部分取自〜2岁的phpBB版本)是否足够好。

如果没有,为什么?
以及您将如何更改它(使现有用户无缝过渡)?

hash_pwd()的结果将保存在数据库中。

function hash_pwd($password)
{
    $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

    $random_state = $this->unique_id();
    $random = '';
    $count = 6;

    if (($fh = @fopen('/dev/urandom', 'rb')))
    {
        $random = fread($fh, $count);
        fclose($fh);
    }

    if (strlen($random) < $count)
    {
        $random = '';

        for ($i = 0; $i < $count; $i += 16)
        {
            $random_state = md5($this->unique_id() . $random_state);
            $random .= pack('H*', md5($random_state));
        }
        $random = substr($random, 0, $count);
    }

    $hash = $this->_hash_crypt_private($password, $this->_hash_gensalt_private($random, $itoa64), $itoa64);

    if (strlen($hash) == 34)
    {
        return $hash;
    }

    return false;
}


function unique_id()
{
    $val = microtime();
    $val = md5($val);

    return substr($val, 4, 16);
}

function _hash_crypt_private($password, $setting, &$itoa64)
{
    $output = '*';

    // Check for correct hash
    if (substr($setting, 0, 3) != '$H$')
    {
        return $output;
    }

    $count_log2 = strpos($itoa64, $setting[3]);

    if ($count_log2 < 7 || $count_log2 > 30)
    {
        return $output;
    }

    $count = 1 << $count_log2;
    $salt = substr($setting, 4, 8);

    if (strlen($salt) != 8)
    {
        return $output;
    }

    /**
    * We're kind of forced to use MD5 here since it's the only
    * cryptographic primitive available in all versions of PHP
    * currently in use.  To implement our own low-level crypto
    * in PHP would result in much worse performance and
    * consequently in lower iteration counts and hashes that are
    * quicker to crack (by non-PHP code).
    */
    if (PHP_VERSION >= 5)
    {
        $hash = md5($salt . $password, true);
        do
        {
            $hash = md5($hash . $password, true);
        }
        while (--$count);
    }
    else
    {
        $hash = pack('H*', md5($salt . $password));
        do
        {
            $hash = pack('H*', md5($hash . $password));
        }
        while (--$count);
    }

    $output = substr($setting, 0, 12);
    $output .= $this->_hash_encode64($hash, 16, $itoa64);

    return $output;
}

function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
{
    if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
    {
        $iteration_count_log2 = 8;
    }

    $output = '$H$';
    $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
    $output .= $this->_hash_encode64($input, 6, $itoa64);

    return $output;
}

function _hash_encode64($input, $count, &$itoa64)
{
    $output = '';
    $i = 0;

    do
    {
        $value = ord($input[$i++]);
        $output .= $itoa64[$value & 0x3f];

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 8;
        }

        $output .= $itoa64[($value >> 6) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 16;
        }

        $output .= $itoa64[($value >> 12) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        $output .= $itoa64[($value >> 18) & 0x3f];
    }
    while ($i < $count);

    return $output;
}

最佳答案

您提供的代码是PHPASS的端口,特别是“便携式”算法。注意portable的限定条件。如果您将phpass作为第二个构造函数参数传递,则这仅适用于true库。从此以后,此答案中的phpass仅将引用引用可移植算法,而不是库本身。如果您未显式指定portable,则默认情况下该库将执行bcrypt。

PHPBB团队并没有自己开发(很好),而是直接从phpass移植了(可争论)。

我们应该在这里提出几个问题:

不好吗?

简短的答案是“不,这还不错”。它提供了很好的安全性。如果您现在对此有代码,那么我就不会着急将其删除。适用于大多数用途。但是话虽如此,如果您开始一个我不会选择的新项目,那么还有更好的选择。

有哪些弱点?

  • 相对于 pbkdf2 :phpass算法使用 hash() ,而pbkdf2()使用 hash_hmac() 。现在,HMAC内部为每个调用运行2个散列,但是PHP实现仅花费对hash()的单个调用执行的1.6倍(C不好吗?)。因此,我们在hash_hmac执行2个散列所需的时间中,有62%的时间从hash()获得2个散列。

    那是什么意思?好吧,对于给定的运行时,pbkdf2将比phpass算法多运行约37.5%散列。给定时间内更多的哈希==好,因为它导致执行更多的计算。

    因此,当使用相同的原语(在这种情况下为pbkdf2)时,37.5%大约比phpassmd5。但是pbkdf2也可以采用更强大的基元。因此,我们可以将pbkdf2sha512结合使用,以获得比phpass算法非常重要的优势(主要是因为sha512是比md5运算量更大的更难算法)。

    这意味着pbkdf2不仅可以在相同的时间内生成更多的计算,而且还可以生成更困难的计算。

    话虽如此,差异并不算太大。这是非常可测量的,并且pbkdf2绝对比phpass“更强”。
  • 相对于bcrypt:比较起来很难。但是,让我们看一下它的表面。 phpass使用md5和PHP中的循环。 pbkdf2使用任何原语(在C中)和PHP中的循环。 bcrypt在C语言中全部使用自定义算法(这意味着它与任何可用的散列算法都不同)。因此,最棒的是,bcrypt仅对算法全部在C中这一事实具有明显的优势。这允许每单位时间进行更多的“计算”。从而使其成为一种更有效的慢速算法(在给定的运行时中进行更多的计算)。

    但是,与执行多少次计算一样重要的是计算的质量。这可能是一篇完整的研究论文,但总而言之,可以归结为一个事实,即bcrypt内部使用的计算比普通的哈希函数执行起来困难得多。
    bcrypt的更强本质的一个示例是bcrypt使用的内部状态比正常的哈希函数大得多的事实。 SHA512使用512位内部状态针对1024位块进行计算。 bcrypt使用大约32kb的内部状态来针对576位的单个块进行计算。 bcrypt的内部状态比SHA512(以及md5phpass)大得多的事实,在一定程度上说明了bcrypt的更强本质。

  • 应该避免

    对于新项目,绝对是。不是说它很坏。不是。事实证明,那里有更强大的算法(数量级)。那么为什么不使用它们呢?

    为了进一步证明bcrypt更强,请查看Slides from Password13 (PDF),它启动了25个GPU集群来破解密码哈希。以下是相关结果:
  • md5($password)
  • 每秒1800亿次猜测
  • 9.4小时-所有可能的8个字符的密码
  • sha1($password)
  • 每秒61亿次猜测
  • 27小时-所有可能的8个字符的密码
  • md5crypt(与phpass非常相似,费用为10):
  • 每秒7700万个猜测
  • 2.5年-所有可能的8个字符的密码
  • bcrypt,费用为5
  • 每秒7万次猜测
  • 2700年-所有可能的8个字符的密码

  • 注意:所有可能的8个字符的密码都使用94个字符集:
    a-zA-Z0-9~`!@#$%^&*()_+-={}|[]\:";'<>,.?/
    

    底线

    因此,如果您要编写新代码,请毫无疑问使用bcrypt。如果现在正在生产中有phpasspbkdf2,则可能要升级,但这并不是明确的“您非常脆弱”。

    关于php - 这在PHP中是一个很好的哈希密码功能吗?如果没有,为什么不呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16042128/

    有关php - 这在PHP中是一个很好的哈希密码功能吗?如果没有,为什么不呢?的更多相关文章

    1. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

      我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

    2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

      使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

    3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    4. ruby-on-rails - 渲染另一个 Controller 的 View - 2

      我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

    5. ruby-on-rails - rails 目前在重启后没有安装 - 2

      我有一个奇怪的问题:我在rvm上安装了ruby​​onrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(

    6. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

      我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

    7. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

      如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

    8. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

      关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

    9. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

      我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

    10. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

      我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

    随机推荐