各位程序猿朋友们,大家好!今天咱们来聊聊WordPress里一个看似不起眼,实则非常重要的函数——check_ajax_referer()
。 这家伙的主要任务是验证AJAX请求中的Nonce,确保你接收到的数据不是来自什么妖魔鬼怪,而是来自你的网站。 咱们今天就来扒一扒它的底裤,看看它到底是怎么玩的。
开场白:Nonce 是个啥?
在正式开始之前,先简单回顾一下Nonce的概念。 Nonce (Number used Once) 是一种安全令牌,就像一次性的密码,用来防止CSRF(跨站请求伪造)攻击。 简单来说,就是当你发起一个请求时,服务器给你一个随机数,你下次发起请求时要把这个数带上,服务器验证这个数是否正确,以此来判断请求是否来自你的网站。
check_ajax_referer()
函数的真面目
check_ajax_referer()
函数位于 wp-includes/functions.php
文件中。 它的基本用法如下:
check_ajax_referer( $action = -1, $query_arg = false, $die = true );
$action
(string|int) (Optional) Action nonce. Default -1. 这是你创建Nonce时使用的action,稍后会详细讲解。$query_arg
(string|bool) (Optional) Key to check for the nonce in the $_REQUEST array. If false, will look for ‘_wpnonce’. Default false. 指定从哪个$_REQUEST
变量中获取Nonce, 默认是_wpnonce
。$die
(bool) (Optional) Whether to die early when the nonce is invalid. Default true. 如果验证失败,是否立即停止脚本执行,默认是true
。
源码深度剖析:一层一层扒开它的心
现在,让我们深入到源码中,看看 check_ajax_referer()
到底做了些什么。
function check_ajax_referer( $action = -1, $query_arg = false, $die = true ) {
if ( false === $query_arg ) {
$query_arg = '_wpnonce';
}
$nonce = '';
if ( isset( $_REQUEST[ $query_arg ] ) ) {
$nonce = $_REQUEST[ $query_arg ];
}
$result = wp_verify_nonce( $nonce, $action );
if ( false === $result && $die ) {
wp_nonce_ays( $action );
exit;
}
return $result;
}
让我们分解一下这段代码:
-
确定Nonce的来源:
if ( false === $query_arg ) { $query_arg = '_wpnonce'; }
这段代码检查
$query_arg
是否为false
。 如果是,则将其设置为'_wpnonce'
。 这意味着,如果你没有明确指定从哪个$_REQUEST
变量中获取Nonce,函数默认会从$_REQUEST['_wpnonce']
中获取。 -
获取Nonce值:
$nonce = ''; if ( isset( $_REQUEST[ $query_arg ] ) ) { $nonce = $_REQUEST[ $query_arg ]; }
这段代码从
$_REQUEST
数组中获取Nonce值。 首先,它检查$query_arg
指定的键是否存在于$_REQUEST
中。 如果存在,则将对应的值赋给$nonce
变量。 如果不存在,$nonce
变量将保持为空字符串。 -
验证Nonce:
$result = wp_verify_nonce( $nonce, $action );
这是最关键的一步。
wp_verify_nonce()
函数负责验证Nonce的有效性。 它接收两个参数:$nonce
(从请求中获取的Nonce值) 和$action
(用于生成Nonce的action)。wp_verify_nonce()
函数会返回1
(如果Nonce有效且在12小时内生成),2
(如果Nonce有效但超过12小时,但在24小时内生成), 或者false
(如果Nonce无效或已过期)。 -
处理验证失败:
if ( false === $result && $die ) { wp_nonce_ays( $action ); exit; }
如果
wp_verify_nonce()
返回false
, 并且$die
参数为true
(默认值),则会调用wp_nonce_ays()
函数并exit
。wp_nonce_ays()
函数会显示一个确认页面,告诉用户请求被拒绝,因为Nonce无效。 这是一个防止CSRF攻击的重要措施。 -
返回验证结果:
return $result;
最后,函数返回
wp_verify_nonce()
的结果。 你可以根据这个结果来决定如何处理请求。
wp_verify_nonce()
函数的内部运作
check_ajax_referer()
依赖于 wp_verify_nonce()
来进行实际的验证。 让我们也简单看看 wp_verify_nonce()
的实现 (位于 wp-includes/pluggable.php
)。
function wp_verify_nonce( $nonce, $action = -1 ) {
$nonce = (string) $nonce;
$action = (string) $action;
$i = wp_nonce_tick();
// Nonce generated 0-12 hours ago
$expected = wp_hash( $i . $action, 'nonce' );
if ( hash_equals( $expected, $nonce ) ) {
return 1;
}
// Nonce generated 12-24 hours ago
$expected = wp_hash( ( $i - 1 ) . $action, 'nonce' );
if ( hash_equals( $expected, $nonce ) ) {
return 2;
}
return false;
}
这段代码的核心逻辑是:
-
计算预期的Nonce值:
$i = wp_nonce_tick(); $expected = wp_hash( $i . $action, 'nonce' );
wp_nonce_tick()
函数返回一个基于时间的整数,它每12小时更新一次。wp_hash()
函数使用这个时间戳、$action
和 ‘nonce’ 作为盐值来生成一个哈希值。 这个哈希值就是我们期望从请求中收到的Nonce值。 -
比较预期的Nonce值和实际收到的Nonce值:
if ( hash_equals( $expected, $nonce ) ) { return 1; }
hash_equals()
函数用于比较两个哈希值,以防止时序攻击。 如果预期的Nonce值和实际收到的Nonce值匹配,则返回1
。 -
检查旧的Nonce值:
$expected = wp_hash( ( $i - 1 ) . $action, 'nonce' ); if ( hash_equals( $expected, $nonce ) ) { return 2; }
为了处理客户端和服务器之间的时间差异,函数还会检查12小时前的Nonce值是否有效。 如果有效,则返回
2
。 -
验证失败:
return false;
如果以上所有检查都失败,则说明Nonce无效,函数返回
false
。
wp_nonce_tick()
函数:时间才是关键
wp_nonce_tick()
函数在Nonce的生成和验证过程中扮演着关键角色。 让我们看看它的实现 (位于 wp-includes/pluggable.php
)。
function wp_nonce_tick() {
/**
* Filters the lifespan of nonces.
*
* @since 4.7.0
*
* @param int $lifespan Nonce lifespan in seconds. Default is 12 hours.
*/
$lifespan = apply_filters( 'nonce_life', DAY_IN_SECONDS / 2 );
return ceil( time() / ( $lifespan ) );
}
这段代码很简单:
-
获取Nonce的有效期:
$lifespan = apply_filters( 'nonce_life', DAY_IN_SECONDS / 2 );
apply_filters( 'nonce_life', DAY_IN_SECONDS / 2 )
允许你通过nonce_life
过滤器来修改Nonce的有效期。 默认情况下,Nonce的有效期是半天 (12小时)。DAY_IN_SECONDS
是一个常量,表示一天有多少秒 (86400)。 -
计算时间戳:
return ceil( time() / ( $lifespan ) );
time()
函数返回当前的时间戳 (从Unix纪元开始的秒数)。 将时间戳除以Nonce的有效期,然后向上取整,得到一个整数。 这个整数就是wp_nonce_tick()
函数的返回值。 这个值每12小时 (默认情况下) 都会改变, 从而保证了Nonce的安全性。
生成 Nonce:wp_create_nonce()
函数
既然我们已经了解了如何验证Nonce,那么也需要知道如何生成Nonce。 WordPress 提供了 wp_create_nonce()
函数来生成Nonce。
$nonce = wp_create_nonce( $action );
$action
参数是一个字符串,用于标识Nonce的目的。 例如,你可以使用 'delete_post_' . $post_id
作为删除文章的Nonce的action。 wp_create_nonce()
函数会返回一个唯一的Nonce值。
让我们看看 wp_create_nonce()
的源码 (位于 wp-includes/pluggable.php
)。
function wp_create_nonce( $action = -1 ) {
$action = (string) $action;
$i = wp_nonce_tick();
return wp_hash( $i . $action, 'nonce' );
}
这段代码非常简单:
-
获取时间戳:
$i = wp_nonce_tick();
调用
wp_nonce_tick()
函数获取当前的时间戳。 -
生成哈希值:
return wp_hash( $i . $action, 'nonce' );
使用时间戳、
$action
和 ‘nonce’ 作为盐值来生成一个哈希值。 这个哈希值就是生成的Nonce值。
把它们串起来:一个完整的 AJAX 示例
现在,让我们通过一个完整的示例来演示如何使用 check_ajax_referer()
函数。
1. PHP 端 (处理 AJAX 请求):
<?php
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() {
// 验证 Nonce
check_ajax_referer( 'my_nonce_action', 'security' );
// 获取传递过来的数据
$data = $_POST['data'];
// 处理数据...
$response = array(
'success' => true,
'message' => '数据已成功处理!',
'data' => $data,
);
// 返回 JSON 格式的响应
wp_send_json( $response );
// Always die in functions echoing AJAX results
die();
}
2. JavaScript 端 (发起 AJAX 请求):
jQuery(document).ready(function($) {
$('#my-button').click(function() {
var data = {
'action': 'my_ajax_action',
'security': my_ajax_object.ajax_nonce, // 从 WordPress 传递过来的 Nonce
'data': 'Hello, world!',
};
$.post(my_ajax_object.ajax_url, data, function(response) {
if (response.success) {
alert(response.message);
} else {
alert('请求失败!');
}
});
});
});
3. 在 WordPress 中传递 Nonce 和 AJAX URL:
<?php
add_action( 'wp_enqueue_scripts', 'enqueue_my_ajax_script' );
function enqueue_my_ajax_script() {
wp_enqueue_script( 'my-ajax-script', get_template_directory_uri() . '/js/my-ajax-script.js', array( 'jquery' ), '1.0', true );
wp_localize_script( 'my-ajax-script', 'my_ajax_object', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'ajax_nonce' => wp_create_nonce( 'my_nonce_action' ),
) );
}
代码解释:
-
PHP 端:
add_action()
函数用于注册 AJAX actions。wp_ajax_my_ajax_action
用于已登录用户,wp_ajax_nopriv_my_ajax_action
用于未登录用户。check_ajax_referer( 'my_nonce_action', 'security' )
验证 Nonce。'my_nonce_action'
是 action,'security'
是$_POST
数组中包含Nonce的键。wp_send_json()
函数用于返回 JSON 格式的响应。die()
函数是必须的,用于停止脚本执行。
-
JavaScript 端:
- 使用 jQuery 的
$.post()
函数发起 AJAX 请求。 my_ajax_object.ajax_url
和my_ajax_object.ajax_nonce
是从 WordPress 传递过来的 AJAX URL 和 Nonce。
- 使用 jQuery 的
-
WordPress 端:
wp_enqueue_script()
函数用于加载 JavaScript 文件。wp_localize_script()
函数用于将 PHP 变量传递给 JavaScript。 在这里,我们将 AJAX URL 和 Nonce 传递给 JavaScript。wp_create_nonce( 'my_nonce_action' )
函数生成 Nonce。'my_nonce_action'
是 action。
重点总结:
为了更好地理解,我们用表格来总结一下 check_ajax_referer()
的关键点:
概念 | 描述 | 作用 |
---|---|---|
Nonce | 一次性使用的随机数,用于防止 CSRF 攻击。 | 验证请求的来源,确保请求来自你的网站。 |
$action |
用于生成和验证 Nonce 的字符串。 必须在生成和验证时使用相同的值。 | 区分不同的 AJAX 请求,提高安全性。 |
wp_create_nonce() |
用于生成 Nonce 的函数。 | 创建 Nonce 值。 |
check_ajax_referer() |
用于验证 AJAX 请求中的 Nonce 的函数。 | 验证 Nonce 的有效性,防止 CSRF 攻击。 |
wp_verify_nonce() |
实际验证 Nonce 的函数。 | 比较预期的 Nonce 值和实际收到的 Nonce 值,判断 Nonce 是否有效。 |
wp_nonce_tick() |
返回一个基于时间的整数,用于 Nonce 的生成和验证。 这个值每 12 小时更新一次 (默认情况下)。 | 保证 Nonce 的有效期,防止重放攻击。 |
$_REQUEST |
用于存储 HTTP 请求参数的数组。 Nonce 通常通过 $_REQUEST['_wpnonce'] 传递。 |
从请求中获取 Nonce 值。 |
实战建议和安全最佳实践:
- 永远不要在客户端生成 Nonce。 Nonce 应该始终在服务器端生成,并传递给客户端。
- 使用唯一的 action。 为每个 AJAX 请求使用不同的 action,以提高安全性。 例如,可以使用
'delete_post_' . $post_id
作为删除文章的Nonce的action。 - 缩短 Nonce 的有效期。 如果你的应用程序对安全性要求很高,可以考虑缩短 Nonce 的有效期。 可以通过
nonce_life
过滤器来修改 Nonce 的有效期。 - 始终验证 Nonce。 在处理 AJAX 请求之前,始终验证 Nonce。
- 使用 HTTPS。 使用 HTTPS 可以加密客户端和服务器之间的通信,防止中间人攻击。
总结:
check_ajax_referer()
函数是 WordPress 中一个重要的安全函数,用于防止 CSRF 攻击。 通过理解其源码和使用方法,可以更好地保护你的 WordPress 网站。 希望今天的讲解能帮助大家更深入地理解 WordPress 的安全机制。 下次再见!