阐述 WordPress 的 `nonce` 机制在 `AJAX` 请求中的应用,并分析 `check_ajax_referer()` 函数的源码。

各位看官,今天咱们不聊风花雪月,就来扒一扒 WordPress 里的“神秘代码”——nonce,尤其是在 AJAX 请求里,这玩意儿可是个保安队长,时刻保护着咱们的数据安全。准备好了吗?咱们这就开讲!

一、Nonce 是个啥?为啥要用它?

想象一下,你家的门钥匙,如果谁都能复制一把,那还得了?Nonce 在 WordPress 里就扮演着类似的角色,但它更聪明,它是一把“一次性钥匙”,用过一次就失效了。

Nonce,全称 "Number used once",顾名思义,就是“用一次的数字”。它是一个随机字符串,主要用来防止 CSRF(Cross-Site Request Forgery,跨站请求伪造)攻击。CSRF 攻击简单来说,就是坏人冒充你,偷偷地替你干坏事,比如发帖子、改密码之类的。

不用 Nonce 的话,坏人就可以构造一个恶意链接,诱骗你点击,然后浏览器就会偷偷地向你的网站发送请求,执行一些你不希望的操作。有了 Nonce,坏人就没那么容易得逞了,因为他不知道这把“一次性钥匙”。

二、NonceAJAX 请求中的应用:保安队长上线!

在 WordPress 的 AJAX 请求中,Nonce 的作用更加重要。因为 AJAX 请求通常是在后台默默地进行的,用户很难察觉,所以更需要保护。

2.1 生成 Nonce:制作钥匙

首先,我们需要生成 Nonce。WordPress 提供了 wp_create_nonce() 函数来完成这个任务。

$nonce = wp_create_nonce( 'my_ajax_action' );

这里的 'my_ajax_action' 是一个动作名称(action),你可以随便起,但一定要有意义,方便你识别。这个动作名称会参与到 Nonce 的生成过程中,相当于给钥匙配了一把锁。

2.2 传递 Nonce:把钥匙交给 AJAX

生成 Nonce 之后,我们需要把它传递给 JavaScript,让 AJAX 请求可以带上这把钥匙。

wp_localize_script( 'my_script', 'my_ajax_object', array(
    'ajax_url' => admin_url( 'admin-ajax.php' ),
    'nonce'    => $nonce,
) );

这段代码的意思是,把 ajax_urlnonce 这两个变量传递给名为 my_script 的 JavaScript 文件,并且把它们放在一个名为 my_ajax_object 的 JavaScript 对象里。

这样,在 JavaScript 文件里,我们就可以通过 my_ajax_object.ajax_urlmy_ajax_object.nonce 来访问这两个变量了。

2.3 发送 AJAX 请求:带着钥匙去开门

有了 Nonce,我们就可以在 AJAX 请求中把它发送到服务器了。

jQuery.ajax({
    url: my_ajax_object.ajax_url,
    type: 'POST',
    data: {
        action: 'my_ajax_action',
        nonce: my_ajax_object.nonce,
        // 其他数据
    },
    success: function(response) {
        // 处理响应
    }
});

注意,这里我们把 Nonce 放在 data 里一起发送到服务器。action 参数也要和生成 Nonce 时使用的动作名称保持一致。

2.4 验证 Nonce:保安队长验明正身

服务器收到 AJAX 请求后,就需要验证 Nonce 的有效性了。WordPress 提供了 check_ajax_referer() 函数来完成这个任务。

add_action( 'wp_ajax_my_ajax_action', 'my_ajax_callback' ); // 登录用户
add_action( 'wp_ajax_nopriv_my_ajax_action', 'my_ajax_callback' ); // 未登录用户

function my_ajax_callback() {
    check_ajax_referer( 'my_ajax_action', 'nonce' );

    // 处理 AJAX 请求
    wp_send_json_success( array( 'message' => '请求成功!' ) );

    wp_die(); // 结束 AJAX 请求
}

这里的 check_ajax_referer( 'my_ajax_action', 'nonce' ) 函数会验证 Nonce 是否有效。第一个参数是动作名称,第二个参数是 Nonce$_POST$_GET 数组中的键名。如果 Nonce 无效,check_ajax_referer() 函数会直接退出,阻止恶意请求。

三、check_ajax_referer() 源码分析:扒开保安队长的外套

咱们现在来深入分析一下 check_ajax_referer() 函数的源码,看看它是如何验证 Nonce 的。

check_ajax_referer() 函数的定义如下:

function check_ajax_referer( $action = -1, $query_arg = false, $die = true ) {
    return wp_verify_nonce( $_REQUEST[ $query_arg ], $action, $die );
}

可以看到,check_ajax_referer() 函数实际上是调用了 wp_verify_nonce() 函数来验证 Nonce 的。它只是简单地从 $_REQUEST 数组中获取 Nonce 的值,然后传递给 wp_verify_nonce() 函数。

wp_verify_nonce() 函数的源码比较复杂,咱们来一步步分析:

function wp_verify_nonce( $nonce, $action = -1 ) {
    $nonce = (string) $nonce;

    $i = wp_nonce_tick();

    // Nonce generated 0-12 hours ago
    $expected = substr( wp_hash( $i . '|' . $action . '|' . get_current_user_id() . '|' . wp_get_session_token(), 'nonce' ), -12, 10 );
    if ( hash_equals( $expected, $nonce ) ) {
        return 1;
    }

    // Nonce generated 12-24 hours ago
    $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . get_current_user_id() . '|' . wp_get_session_token(), 'nonce' ), -12, 10 );
    if ( hash_equals( $expected, $nonce ) ) {
        return 2;
    }

    // Invalid nonce
    return false;
}

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

  1. 获取时间戳: wp_nonce_tick() 函数返回一个基于时间戳的数字,这个数字每 12 小时会更新一次。

    function wp_nonce_tick() {
        /**
         * Filters the lifespan of nonces.
         *
         * @since 3.5.0
         *
         * @param int $lifespan Nonce lifespan in seconds. Default 12 hours.
         */
        $lifespan = apply_filters( 'nonce_life', DAY_IN_SECONDS / 2 );
    
        return ceil( time() / ( $lifespan / 2 ) );
    }

    可以看到,默认情况下,Nonce 的有效期是 12 小时。wp_nonce_tick() 函数返回的是当前时间戳除以 12 小时得到的结果,向上取整。

  2. 生成期望的 Nonce wp_hash() 函数使用 md5 算法对一个字符串进行哈希,然后取哈希值的后 10 位作为期望的 Nonce。这个字符串由以下几个部分组成:

    • $i:当前的时间戳。
    • $action:动作名称。
    • get_current_user_id():当前用户的 ID。
    • wp_get_session_token():当前用户的会话令牌。

    这些信息组合在一起,可以唯一地标识一个 Nonce

  3. 比较 Nonce hash_equals() 函数用于比较用户传递的 Nonce 和期望的 Nonce 是否相等。hash_equals() 函数可以防止时序攻击,保证比较的安全性。

  4. 检查过期 Nonce 为了防止由于服务器时间和客户端时间不一致导致 Nonce 验证失败,wp_verify_nonce() 函数还会检查前一个 12 小时生成的 Nonce 是否有效。

如果用户传递的 Nonce 和当前时间戳或前一个时间戳生成的 Nonce 相等,wp_verify_nonce() 函数就会返回 12,表示 Nonce 验证成功。否则,返回 false,表示 Nonce 验证失败。

四、总结:安全第一,Nonce 护航!

Nonce 机制是 WordPress 中非常重要的安全机制,它可以有效地防止 CSRF 攻击,保护用户的数据安全。在 AJAX 请求中,Nonce 的作用更加重要,因为 AJAX 请求通常是在后台默默地进行的,用户很难察觉。

步骤 说明 函数/代码
1. 生成 Nonce 使用 wp_create_nonce() 函数生成 Nonce。 $nonce = wp_create_nonce( 'my_ajax_action' );
2. 传递 Nonce 使用 wp_localize_script() 函数将 Nonce 传递给 JavaScript。 wp_localize_script( 'my_script', 'my_ajax_object', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => $nonce, ) );
3. 发送请求 在 AJAX 请求中将 Nonce 发送到服务器。 jQuery.ajax({ url: my_ajax_object.ajax_url, type: 'POST', data: { action: 'my_ajax_action', nonce: my_ajax_object.nonce, // 其他数据 }, success: function(response) { // 处理响应 } });
4. 验证 Nonce 使用 check_ajax_referer() 函数验证 Nonce 的有效性。 check_ajax_referer( 'my_ajax_action', 'nonce' );
wp_verify_nonce 源码 验证 Nonce 的核心函数,校验时间戳、动作名称、用户ID和会话令牌。 function wp_verify_nonce( $nonce, $action = -1 ) { ... }

在使用 Nonce 时,需要注意以下几点:

  • 动作名称要唯一: 不同的 AJAX 请求应该使用不同的动作名称,防止 Nonce 被滥用。
  • Nonce 的有效期: 默认情况下,Nonce 的有效期是 12 小时。你可以通过 nonce_life 过滤器来修改 Nonce 的有效期。
  • Nonce 的安全性: Nonce 本身并不是绝对安全的,它只能防止 CSRF 攻击。如果你的网站存在 XSS 漏洞,攻击者仍然可以获取到 Nonce,然后利用它来发起攻击。因此,除了使用 Nonce 之外,还需要采取其他安全措施,比如过滤用户输入、使用 HTTPS 等。

好了,今天的讲座就到这里。希望大家能够理解 Nonce 机制的原理和应用,并在自己的 WordPress 项目中合理地使用 Nonce,保护网站的数据安全!记住,安全无小事,咱们要时刻保持警惕!

下次有机会,咱们再聊聊 WordPress 的其他“神秘代码”,不见不散!

发表回复

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