深入解读 WordPress `wp_verify_nonce()` 函数源码:`Nonce` 有效性验证与对比原理。

咳咳,各位观众老爷们,晚上好!我是老码,今儿咱不开车,就聊聊 WordPress 里的一个“小秘密”—— wp_verify_nonce() 函数。 别看它名字挺唬人,其实干的活儿挺实在,就是帮咱们验证一下“令牌”是不是真的,有没有被篡改。 说白了,就是个门卫大爷,负责盘查身份。

第一章: 啥是 Nonce?为什么要验证?

在深入 wp_verify_nonce() 之前,咱们先得搞明白啥是 Nonce。 Nonce 这玩意儿,英文全称是 "Number used once",直译过来就是“只用一次的数字”。 在 Web 安全领域,它通常指一个随机生成的字符串,用于防止 CSRF (Cross-Site Request Forgery) 攻击。

  • CSRF 攻击是啥? 简单来说,就是坏人冒充你,向服务器发送请求,比如修改你的个人资料,或者偷偷发个帖子啥的。

  • Nonce 怎么防 CSRF? 原理很简单,就是给每个关键操作都加上一个随机的“暗号”,只有知道这个暗号的人才能成功执行操作。 这个暗号就是 Nonce。

举个栗子:

假设你想删除一篇博客文章。 正常的流程是:

  1. 你登录 WordPress 后台。
  2. 你点击“删除”按钮。
  3. WordPress 服务器收到你的删除请求,然后删除文章。

如果有了 CSRF 攻击,坏人可能会构造一个链接,比如:https://yourwebsite.com/wp-admin/post.php?action=delete&post=123, 诱骗你点击。 如果你恰好登录了 WordPress 后台,那么这个链接就会在你的浏览器中执行,从而删除你的文章。

但是,如果我们使用了 Nonce,情况就不一样了。 删除链接会变成类似这样:

https://yourwebsite.com/wp-admin/post.php?action=delete&post=123&_wpnonce=e7a8b2c3d4

这里,_wpnonce 就是 Nonce 值。 只有知道这个 Nonce 值的人才能成功删除文章。 由于 Nonce 是随机生成的,坏人很难猜到,所以可以有效防止 CSRF 攻击。

为什么要验证 Nonce?

这还用问? Nonce 是用来保护咱们的数据安全的,如果不验证,那 Nonce 就成了摆设,没有任何意义。 验证 Nonce 就是确保请求确实来自合法的用户,而不是坏人的伪造。

第二章: wp_verify_nonce() 函数的真面目

好了,铺垫了这么多,终于要轮到咱们的主角 wp_verify_nonce() 登场了。 这个函数的作用就是验证 Nonce 值是否有效。 它的用法很简单:

<?php
$nonce = $_POST['_wpnonce']; // 从 POST 请求中获取 Nonce 值
$action = 'my_custom_action'; // 定义一个 action,用于生成 Nonce

if ( wp_verify_nonce( $nonce, $action ) ) {
    // Nonce 验证通过,执行操作
    echo 'Nonce 验证通过!';
} else {
    // Nonce 验证失败,拒绝操作
    echo 'Nonce 验证失败!';
}
?>
  • $nonce: 要验证的 Nonce 值。
  • $action: 用于生成 Nonce 的 action。 这个 action 就像一个“盐”,可以增加 Nonce 的安全性。

wp_verify_nonce() 函数返回值的意义:

返回值 含义
1 Nonce 验证通过,且 Nonce 仍然有效。 默认情况下,Nonce 的有效期是 12 个小时。
2 Nonce 验证通过,但是 Nonce 已经过期。 也就是说,Nonce 超过了有效期。
false Nonce 验证失败。 可能是 Nonce 值不正确,或者 action 不匹配。

第三章: wp_verify_nonce() 源码剖析

光会用还不够,咱们还得知道 wp_verify_nonce() 内部是怎么运作的。 这样才能更好地理解 Nonce 的原理,以及如何正确使用它。

wp_verify_nonce() 函数的代码位于 wp-includes/pluggable.php 文件中。 咱们来简化一下,看看它的核心逻辑:

<?php
function wp_verify_nonce( $nonce, $action = -1 ) {
    $nonce = (string) $nonce; // 确保 nonce 是字符串
    $i = wp_nonce_tick(); // 获取当前时间片段

    // Hash the tokens and hide them concatinated behind the tick.
    $expected = substr( wp_hash( $i . $action . get_current_user_id(), 'nonce' ), -12, 12 );
    if ( hash_equals( $expected, $nonce ) ) {
        return 1;
    }

    $expected = substr( wp_hash( ( $i - 1 ) . $action . get_current_user_id(), 'nonce' ), -12, 12 );
    if ( hash_equals( $expected, $nonce ) ) {
        return 2;
    }

    return false;
}
?>

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

  1. 获取当前时间片段: wp_nonce_tick() 函数会返回一个基于时间戳的数字,用于增加 Nonce 的随机性。 默认情况下,这个时间片段会每 12 个小时更新一次。
  2. 生成预期 Nonce: 使用 wp_hash() 函数,将时间片段、action 和用户 ID 组合起来,生成一个哈希值。 然后取哈希值的最后 12 位作为预期的 Nonce 值。
  3. 比较 Nonce: 将传入的 Nonce 值与预期的 Nonce 值进行比较。 如果相等,则验证通过。
  4. 考虑过期情况: 为了允许一定的时间误差,wp_verify_nonce() 还会验证前一个时间片段生成的 Nonce 值。 如果前一个时间片段的 Nonce 值也匹配,则验证通过,但返回 2,表示 Nonce 已经过期。

几个关键函数的解读:

  • wp_nonce_tick() 这个函数返回一个基于时间戳的数字,用于增加 Nonce 的随机性。 它的代码如下:

    <?php
    function wp_nonce_tick() {
        /**
         * Filters the lifespan of nonces.
         *
         * @since 4.7.0
         *
         * @param int $lifespan The lifespan in seconds. Default is DAY_IN_SECONDS.
         */
        $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS );
    
        return ceil( time() / ( $nonce_life / 2 ) );
    }
    ?>

    可以看到,wp_nonce_tick() 函数会获取当前时间戳,然后除以 Nonce 的生命周期的一半。 默认情况下,Nonce 的生命周期是 DAY_IN_SECONDS,也就是 86400 秒(一天)。 所以,wp_nonce_tick() 函数会每 12 个小时返回一个不同的数字。 你可以通过 nonce_life 过滤器修改这个生命周期。

  • wp_hash() 这个函数用于生成哈希值。 它的代码如下:

    <?php
    function wp_hash( $data, $scheme = 'auth' ) {
        static $wp_hasher;
    
        if ( empty( $wp_hasher ) ) {
            require_once ABSPATH . 'wp-includes/class-phpass.php';
            $wp_hasher = new PasswordHash( 8, true );
        }
    
        return $wp_hasher->HashPassword( $data . $scheme );
    }
    ?>

    wp_hash() 函数使用了 PasswordHash 类来生成哈希值。 PasswordHash 类是一个可移植的密码哈希框架,可以用于生成安全的密码哈希值。

  • hash_equals() 这个函数用于比较两个字符串是否相等,并且可以防止时序攻击。 时序攻击是指攻击者通过测量比较两个字符串所需的时间来推断字符串的内容。 hash_equals() 函数会确保比较两个字符串所需的时间是恒定的,从而防止时序攻击。

第四章: 如何正确使用 Nonce?

了解了 wp_verify_nonce() 函数的原理,接下来咱们聊聊如何正确使用 Nonce。

  1. 为每个关键操作生成 Nonce: 比如,删除文章、修改个人资料、发表评论等等。

  2. 使用不同的 action: 为不同的操作使用不同的 action,可以增加 Nonce 的安全性。 比如,删除文章的 action 可以是 'delete_post',修改个人资料的 action 可以是 'update_profile'

  3. 在表单中包含 Nonce: 可以使用 wp_nonce_field() 函数在表单中自动生成 Nonce 字段。 例如:

    <?php
    wp_nonce_field( 'my_custom_action', '_wpnonce' );
    ?>

    这会在表单中生成一个隐藏字段,包含 Nonce 值:

    <input type="hidden" id="_wpnonce" name="_wpnonce" value="e7a8b2c3d4" />
  4. 在 URL 中包含 Nonce: 可以使用 wp_nonce_url() 函数在 URL 中生成 Nonce 参数。 例如:

    <?php
    $delete_url = wp_nonce_url( 'post.php?action=delete&post=123', 'delete_post' );
    ?>

    这会生成一个包含 Nonce 参数的 URL:

    post.php?action=delete&post=123&_wpnonce=f8b9c4d5e6

  5. 验证 Nonce: 在处理请求时,使用 wp_verify_nonce() 函数验证 Nonce 值是否有效。

  6. 注意 Nonce 的有效期: 默认情况下,Nonce 的有效期是 12 个小时。 如果需要更长的有效期,可以使用 nonce_life 过滤器修改。

第五章: Nonce 的安全性讨论

虽然 Nonce 可以有效防止 CSRF 攻击,但它并不是万能的。

  • Nonce 依赖于用户的登录状态: 如果用户没有登录,Nonce 就无法发挥作用。 因为 wp_verify_nonce() 函数会使用用户 ID 来生成 Nonce,如果没有用户 ID,就无法生成有效的 Nonce。
  • Nonce 需要保密: 如果 Nonce 被泄露,坏人就可以利用它来伪造请求。 因此,Nonce 应该保存在服务器端,不要暴露给客户端。
  • Nonce 的有效期有限: 如果 Nonce 过期,就无法使用。 因此,需要定期更新 Nonce。

第六章: 实战案例分析

咱们来分析一个实际的例子,看看如何在 WordPress 中使用 Nonce。

假设我们要创建一个自定义的表单,用于修改用户的个人资料。

  1. 生成 Nonce: 在表单中添加一个 Nonce 字段:

    <?php
    <form method="post" action="">
        <?php wp_nonce_field( 'update_profile', '_wpnonce' ); ?>
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" value="<?php echo esc_attr( $user->user_login ); ?>" /><br />
        <label for="email">邮箱:</label>
        <input type="email" id="email" name="email" value="<?php echo esc_attr( $user->user_email ); ?>" /><br />
        <input type="submit" value="保存" />
    </form>
    ?>
  2. 验证 Nonce: 在处理表单提交时,验证 Nonce 值:

    <?php
    if ( isset( $_POST['submit'] ) ) {
        if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'update_profile' ) ) {
            wp_die( 'Nonce 验证失败!' );
        }
    
        // Nonce 验证通过,更新用户资料
        $username = sanitize_user( $_POST['username'] );
        $email = sanitize_email( $_POST['email'] );
    
        wp_update_user( array( 'ID' => $user->ID, 'user_login' => $username, 'user_email' => $email ) );
    
        echo '资料更新成功!';
    }
    ?>

这个例子演示了如何使用 wp_nonce_field() 函数生成 Nonce,以及如何使用 wp_verify_nonce() 函数验证 Nonce。

第七章:总结

好了,今天的讲座就到这里。 咱们深入剖析了 WordPress 的 wp_verify_nonce() 函数,了解了 Nonce 的原理、作用和使用方法。 希望大家以后在开发 WordPress 插件和主题时,能够正确使用 Nonce,保护咱们的数据安全。

记住,安全无小事! 多一层防护,就少一分风险。

感谢各位观众老爷的捧场! 下次再见!

发表回复

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