【问题标题】:Validate a password against an SSHA256 hash in PHP根据 PHP 中的 SSHA256 哈希验证密码
【发布时间】:2013-02-02 01:22:28
【问题描述】:

为了使用 Dovecot 进行身份验证,我使用 SSHA256 哈希,但我不知道如何根据现有哈希验证给定密码。以下 PHP 函数(在网上找到)用于创建 SSHA256 哈希:

function ssha256($pw) {
        $salt = make_salt();
        return "{SSHA256}" . base64_encode( hash('sha256', $pw . $salt, true ) . $salt );
}

function make_salt() {
        $len   = 4;
        $bytes = array();
        for ($i = 0; $i < $len; $i++ ) {
                $bytes[] = rand(1,255);
        }
        $salt_str = '';
        foreach ($bytes as $b) {
                $salt_str .= pack('C', $b);
        }
        return $salt_str;
}

示例输出:{SSHA256}lGq49JTKmBC49AUrk7wLyQVmeZ7cGl/V13A9QbY4RVKchckL

我是否必须提取盐,但如何提取? 我完全迷失了解决问题的方法,有人提示吗?

感谢大家的帮助!

抱歉,我必须使用 SSHA256,因为 Dovecot 1.2.15 仅支持以下方案: CRYPT MD5 MD5-CRYPT SHA SHA1 SHA256 SMD5 SSHA SSHA256 PLAIN CLEARTEXT CRAM-MD5 HMAC-MD5 DIGEST-MD5 PLAIN-MD4 PLAIN-MD5 LDAP-MD5 LANMAN NTLM OTP SKEY RPA

【问题讨论】:

  • 您应该使用加密安全的盐和迭代哈希。更好的是,只需使用 bcrypt,它会为您完成所有这些工作。

标签: php validation passwords salt


【解决方案1】:

您不应将 SHA 系列用于密码散列。它们快速,专为快速散列文件而设计。你需要密码散列是昂贵的。使用 bcrypt、PHPass 或只使用我自己推出的这个类(但直到你学会在其中挖洞):

class PassHash {
    public static function rand_str($length) {
        $total = $length % 2;
        $output = "";
        if ($total !== 0) {
            $count = floor($length / 2);
            $output .= ".";
        } else $count = $length / 2;

        $bytes = openssl_random_pseudo_bytes($count);
        $output .= bin2hex($bytes);

        // warning: prepending with a dot if the length is odd.
        // this can be very dangerous. no clue why you'd want your
        // bcrypt salt to do this, but /shrug

        return $output;
    }
    // 2y is an exploit fix, and an improvement over 2a. Only available in 5.4.0+
    public static function hash($input) {
        return crypt($input, "$2y$13$" . self::rand_str(22));

    }

    // legacy support, add exception handling and fall back to <= 5.3.0
    public static function hash_weak($input) {
        return crypt($input, "$2a$13$" . self::rand_str(22));
    }

    public static function compare($input, $hash) {
        return (crypt($input, $hash) === $hash);
    }
}

您必须对给定的明文进行哈希处理,并将该哈希值与您存储的哈希值进行比较。盐存储在哈希中,并且应该是随机的。如果你喜欢,加一个胡椒粉。您还应该使工作率可变,以便您可以在需要时随时更改工作率并保持系统正常工作。


如果像你说的那样,你无法实现这个,你可以按如下方式解压散列:

function unpack_hash($hash) {
        $hash = base64_decode($hash);
        $split = str_split($hash, 64);
        return array("salt" => $split[1], "hash" => $split[0]);

这是因为 SHA256 是 256 位或 64 个十六进制字符。您总是可以假设前 64 个字符是哈希

【讨论】:

  • 您好,非常感谢您的回答!我的所有脚本都使用 Bcrypt,但不幸的是 Dovecot 1.2 不支持它。以下是支持的方案: CRYPT MD5 MD5-CRYPT SHA SHA1 SHA256 SMD5 SSHA SSHA256 PLAIN CLEARTEXT CRAM-MD5 HMAC-MD5 DIGEST-MD5 PLAIN-MD4 PLAIN-MD5 LDAP-MD5 LANMAN NTLM OTP SKEY RPA
  • 哎哟。没有机会添加支持?
  • 非常感谢您的帮助!根据this question,我将盐替换为uniqid()。哦,我不得不删除括号:str_replace('{SSHA256}', '', $hash); 谢谢!
  • @Fips - 我在dovecot documentation 中发现了一个提示,在某些情况下通过 CRYPT 方案支持迭代散列。如果你的系统支持这个,你真的应该使用这个方案。关于盐,加密强盐应该来自操作系统随机源,因此是唯一且不可预测的,另一边的uniqid()或多或少是一个时间戳。
  • 谢谢,我去看看,现在我会换盐。
【解决方案2】:

您需要将盐与哈希值一起存储。

当您需要验证密码时,您只需使用用户输入密码 + 存储的盐值再次计算哈希。如果哈希匹配,则用户输入了正确的密码。

对于您的格式,首先使用base64_decode,结果的最后 4 个字节将是盐。

【讨论】:

  • 您好,感谢您的快速回答!盐实际上是与哈希一起存储的,但我不知道如何将其从 Base64 中打包的原始格式中取出。
  • 看,盐被附加到散列(原始输出),然后整个东西都用 Base64 编码。当我尝试使用 base64_decode 时,我得到了二进制数据,但我不知道如何处理它。
  • 二进制数据的最后 4 个字节是盐,其余的是哈希。
  • 更具体地说,LDAP 样式的 salt 是不是散列的所有字节。可以是任意长度:issues.apache.org/jira/browse/DIRSERVER-1375
猜你喜欢
  • 1970-01-01
  • 2011-06-20
  • 1970-01-01
  • 2012-11-05
  • 2016-08-27
  • 1970-01-01
  • 1970-01-01
  • 2015-01-31
相关资源
最近更新 更多