各位看官,今天咱们不聊风花雪月,就来扒一扒 WordPress 里的“神秘代码”——nonce
,尤其是在 AJAX
请求里,这玩意儿可是个保安队长,时刻保护着咱们的数据安全。准备好了吗?咱们这就开讲!
一、Nonce
是个啥?为啥要用它?
想象一下,你家的门钥匙,如果谁都能复制一把,那还得了?Nonce
在 WordPress 里就扮演着类似的角色,但它更聪明,它是一把“一次性钥匙”,用过一次就失效了。
Nonce
,全称 "Number used once",顾名思义,就是“用一次的数字”。它是一个随机字符串,主要用来防止 CSRF(Cross-Site Request Forgery,跨站请求伪造)攻击。CSRF 攻击简单来说,就是坏人冒充你,偷偷地替你干坏事,比如发帖子、改密码之类的。
不用 Nonce
的话,坏人就可以构造一个恶意链接,诱骗你点击,然后浏览器就会偷偷地向你的网站发送请求,执行一些你不希望的操作。有了 Nonce
,坏人就没那么容易得逞了,因为他不知道这把“一次性钥匙”。
二、Nonce
在 AJAX
请求中的应用:保安队长上线!
在 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_url
和 nonce
这两个变量传递给名为 my_script
的 JavaScript 文件,并且把它们放在一个名为 my_ajax_object
的 JavaScript 对象里。
这样,在 JavaScript 文件里,我们就可以通过 my_ajax_object.ajax_url
和 my_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;
}
这段代码做了以下几件事:
-
获取时间戳:
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 小时得到的结果,向上取整。 -
生成期望的
Nonce
:wp_hash()
函数使用md5
算法对一个字符串进行哈希,然后取哈希值的后 10 位作为期望的Nonce
。这个字符串由以下几个部分组成:$i
:当前的时间戳。$action
:动作名称。get_current_user_id()
:当前用户的 ID。wp_get_session_token()
:当前用户的会话令牌。
这些信息组合在一起,可以唯一地标识一个
Nonce
。 -
比较
Nonce
:hash_equals()
函数用于比较用户传递的Nonce
和期望的Nonce
是否相等。hash_equals()
函数可以防止时序攻击,保证比较的安全性。 -
检查过期
Nonce
: 为了防止由于服务器时间和客户端时间不一致导致Nonce
验证失败,wp_verify_nonce()
函数还会检查前一个 12 小时生成的Nonce
是否有效。
如果用户传递的 Nonce
和当前时间戳或前一个时间戳生成的 Nonce
相等,wp_verify_nonce()
函数就会返回 1
或 2
,表示 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 的其他“神秘代码”,不见不散!