深入理解 WordPress `wp_verify_password()` 函数的源码:它是如何兼容 `phpass` 库的密码验证的。

各位听众,早上好!今天咱们来聊聊 WordPress 密码验证的那些事儿,特别是 wp_verify_password() 这位幕后英雄是如何跟古老的 phpass 库“眉来眼去”的。放心,保证通俗易懂,不会让你听得打瞌睡。

开场白:密码,安全的基石

密码,那是我们网络世界的钥匙,保护着我们的数据和隐私。在 WordPress 这种内容管理系统中,密码更是重中之重。用户登录、权限验证,都离不开它。但是,密码存储和验证可不是简单地把密码明文存起来再比对一下就完事儿了。如果真是那样,黑客们做梦都会笑醒。

wp_verify_password():WordPress 的密码验证官

wp_verify_password() 函数,就是 WordPress 负责密码验证的官员。它的主要任务是:拿着用户输入的密码,跟数据库里存储的密码哈希值进行比对,判断密码是否正确。

先来看看 wp-includes/pluggable.php 文件中 wp_verify_password() 的基本结构:

function wp_verify_password( $password, $hash ) {
    global $wp_hasher;

    if ( empty( $password ) || empty( $hash ) ) {
        return false;
    }

    /**
     * Filter whether to short-circuit the password verification, returning the result.
     *
     * @since 3.5.0
     *
     * @param bool   $verified Whether to short-circuit verification. Default null.
     * @param string $password The user-supplied password to check.
     * @param string $hash     The stored password hash to check against.
     */
    $verified = apply_filters( 'check_password', null, $password, $hash );

    if ( null !== $verified ) {
        return $verified;
    }

    // If the hash is still using MD5, re-hash it to bcrypt.
    if ( strpos( $hash, '$P$' ) === 0 ) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash( 8, true );
        return $wp_hasher->CheckPassword( $password, $hash );
    }

    if ( strpos( $hash, '$H$' ) === 0 ) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash( 8, true );
        return $wp_hasher->CheckPassword( $password, $hash );
    }

    if ( empty( $wp_hasher ) ) {
        require_once ABSPATH . WPINC . '/class-wp-password-hasher.php';
        $wp_hasher = new WP_Password_Hasher();
    }

    return $wp_hasher->verify( $password, $hash );
}

代码并不长,但信息量很大。我们一步一步来分析。

第一步:安全检查与过滤器

if ( empty( $password ) || empty( $hash ) ) {
    return false;
}

/**
 * Filter whether to short-circuit the password verification, returning the result.
 * ...
 */
$verified = apply_filters( 'check_password', null, $password, $hash );

if ( null !== $verified ) {
    return $verified;
}

这部分代码主要做了两件事:

  1. 空值检查: 如果用户输入的密码或者数据库里存储的哈希值是空的,那就直接返回 false,不用往下验证了。毕竟,空密码肯定是不行的。
  2. 过滤器: WordPress 提供了一个名为 check_password 的过滤器,允许开发者自定义密码验证逻辑。如果某个插件或主题使用了这个过滤器,并且返回了非空值,那么 wp_verify_password() 就会直接返回过滤器的结果,不再执行默认的验证流程。这为开发者提供了极大的灵活性。

第二步:phpass 时代的遗产

if ( strpos( $hash, '$P$' ) === 0 ) {
    require_once ABSPATH . WPINC . '/class-phpass.php';
    $wp_hasher = new PasswordHash( 8, true );
    return $wp_hasher->CheckPassword( $password, $hash );
}

if ( strpos( $hash, '$H$' ) === 0 ) {
    require_once ABSPATH . WPINC . '/class-phpass.php';
    $wp_hasher = new PasswordHash( 8, true );
    return $wp_hasher->CheckPassword( $password, $hash );
}

这部分代码是今天讲座的重点,它处理的是与 phpass 库的兼容问题。

  • phpass 是什么?

    phpass 是一个可移植的密码哈希框架,由 Solar Designer 开发。它被广泛应用于 PHP 项目中,用于安全地存储密码。在早期版本的 WordPress 中,phpass 是默认的密码哈希库。

  • 为什么要兼容 phpass

    因为历史原因。早期的 WordPress 版本使用 phpass 来哈希密码。为了保证老用户的密码仍然能够正常登录,WordPress 需要继续支持 phpass 格式的密码哈希值。

  • $P$$H$ 是什么?

    这两个字符串是 phpass 生成的密码哈希值的前缀,用于标识使用了哪种哈希算法。$P$ 表示使用 MD5 哈希算法,$H$ 表示使用 MD5 扩展哈希算法。它们的存在让 wp_verify_password() 能够识别出哪些密码哈希值是使用 phpass 生成的。

  • 这段代码做了什么?

    1. 检查哈希值的前缀: 使用 strpos() 函数检查哈希值是否以 $P$$H$ 开头。
    2. 加载 phpass 库: 如果哈希值的前缀是 $P$$H$,则加载 class-phpass.php 文件,该文件包含了 phpass 库的实现。
    3. 创建 PasswordHash 对象: 创建一个 PasswordHash 对象,并传入哈希强度和可移植性参数。这里的 8 表示哈希强度,true 表示可移植性。
    4. 调用 CheckPassword() 方法: 调用 PasswordHash 对象的 CheckPassword() 方法,传入用户输入的密码和数据库里存储的哈希值,进行密码验证。

phpass 的细节:哈希算法与盐值

phpass 的核心在于使用了盐值(salt)来增加密码的安全性。盐值是一个随机字符串,与密码混合后进行哈希。这样,即使黑客获得了密码哈希值,也无法通过彩虹表等方法破解密码,因为每个密码都使用了不同的盐值。

phpass 使用的哈希算法主要是 MD5,虽然现在 MD5 已经被认为是不安全的哈希算法,但在当时,它仍然是一种相对安全的方案。而且,phpass 通过多次迭代哈希和使用盐值,进一步提高了密码的安全性。

第三步:现代密码哈希方案

if ( empty( $wp_hasher ) ) {
    require_once ABSPATH . WPINC . '/class-wp-password-hasher.php';
    $wp_hasher = new WP_Password_Hasher();
}

return $wp_hasher->verify( $password, $hash );

这部分代码处理的是使用现代密码哈希方案的密码验证。

  • WP_Password_Hasher 是什么?

    WP_Password_Hasher 是 WordPress 3.5 引入的密码哈希类,它使用了 bcrypt 算法,这是一种更加安全的密码哈希算法。

  • 为什么要引入 WP_Password_Hasher

    因为 MD5 已经不再安全。为了提高密码的安全性,WordPress 需要使用更强的密码哈希算法。bcrypt 算法被认为是目前最安全的密码哈希算法之一,因此 WordPress 选择了它。

  • 这段代码做了什么?

    1. 检查 $wp_hasher 对象: 检查全局变量 $wp_hasher 是否为空。如果为空,则说明还没有创建 WP_Password_Hasher 对象。
    2. 加载 WP_Password_Hasher 类: 加载 class-wp-password-hasher.php 文件,该文件包含了 WP_Password_Hasher 类的实现。
    3. 创建 WP_Password_Hasher 对象: 创建一个 WP_Password_Hasher 对象。
    4. 调用 verify() 方法: 调用 WP_Password_Hasher 对象的 verify() 方法,传入用户输入的密码和数据库里存储的哈希值,进行密码验证。

WP_Password_Hasher 的细节:bcrypt 算法的优势

bcrypt 算法相比 MD5 算法,具有以下优势:

  • 自适应性: bcrypt 算法的计算复杂度可以根据硬件性能进行调整,从而防止暴力破解。
  • 抗碰撞性: bcrypt 算法的抗碰撞性更强,即使不同的密码生成了相同的哈希值,也很难被利用。
  • 盐值: bcrypt 算法也使用盐值,进一步提高了密码的安全性。

密码升级:从 phpass 到 bcrypt

为了提高安全性,WordPress 会在用户登录时,自动将 phpass 格式的密码哈希值升级到 bcrypt 格式。这个过程是透明的,用户不会感觉到任何变化。

总结:wp_verify_password() 的职责

用一张表格来总结一下 wp_verify_password() 的职责:

步骤 描述 涉及的技术
1 安全检查:检查密码和哈希值是否为空。
2 过滤器:允许开发者自定义密码验证逻辑。 WordPress 过滤器 API
3 phpass 兼容:如果哈希值是 phpass 格式,则使用 phpass 库进行验证。 phpass 库,MD5 哈希算法,盐值
4 bcrypt 验证:如果哈希值是 bcrypt 格式,则使用 WP_Password_Hasher 类进行验证。 bcrypt 算法,盐值
5 密码升级:如果用户使用 phpass 格式的密码登录,则自动升级到 bcrypt 格式。 phpass 库,bcrypt 算法

代码示例:WP_Password_Hasherverify() 方法

为了更深入地了解 WP_Password_Hasher 的工作原理,我们来看一下 class-wp-password-hasher.php 文件中 verify() 方法的简化版代码:

class WP_Password_Hasher {
    public function verify( $password, $hash ) {
        // Check if hash is a portable hash from before version 2.5.1.
        if ( strlen( $hash ) == 32 ) {
            return $this->check_password( $password, $hash );
        }

        if ( strpos( $hash, '$2y$' ) !== 0 && strpos( $hash, '$2a$' ) !== 0 ) {
            return false;
        }

        return password_verify( $password, $hash );
    }

    public function check_password( $password, $hash ) {
        $check = hash_equals( md5( $password ), $hash );

        /**
         * Fires when the legacy MD5 password check is performed.
         *
         * @since 4.4.0
         *
         * @param bool $check Whether the password matches the hash.
         */
        do_action( 'check_password_reset_after_key', $check );

        return $check;
    }

}

这段代码主要做了以下几件事:

  1. 检查哈希长度: 如果哈希长度是32,说明是老版本的MD5哈希,调用check_password进行验证。
  2. bcrypt 格式验证: 使用 PHP 内置的 password_verify() 函数来验证密码。password_verify() 函数会自动处理 bcrypt 算法的细节,包括盐值的提取和哈希计算。
  3. 老旧MD5密码验证 check_password使用hash_equals函数来防止时序攻击.

总结与展望

wp_verify_password() 函数是 WordPress 密码验证的核心,它兼容了古老的 phpass 库,并使用了现代的 bcrypt 算法,保证了用户密码的安全性。

随着技术的发展,密码安全面临的挑战也越来越大。未来,WordPress 可能会引入更先进的密码哈希算法,例如 Argon2,以应对新的安全威胁。

希望今天的讲座能够帮助大家更好地理解 WordPress 密码验证的原理。谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注