分析 WordPress `wp_verify_nonce()` 函数的源码:它是如何通过 `Nonce` 令牌验证请求的有效性的。

各位攻城狮、程序媛们,早上好!今天咱们来聊聊WordPress里一个看似不起眼,实则关系到网站安全的重要函数:wp_verify_nonce()。它就像网站的“门卫”,负责检查每一个请求,看看是不是“自己人”发的,防止坏人来搞事情。

Nonce 是什么?为什么要验证?

在深入wp_verify_nonce()之前,我们需要先理解Nonce的概念。Nonce,全称"Number used Once",顾名思义,就是一个只用一次的数字。在WordPress中,Nonce就是一个随机生成的字符串,用于验证请求的真实性。

想象一下,你经营一家咖啡馆,熟客来买咖啡,你认得他们,直接给他们做就行。但如果来了个陌生人,你怎么确定他不是来白嫖的呢?你可以让他说出一个只有熟客才知道的“暗号”,这个“暗号”就是Nonce。

为什么要验证Nonce呢?因为互联网上有很多坏人,他们会伪造请求,比如冒充用户提交表单、删除文章等等。如果没有Nonce验证,他们就可以为所欲为,网站就完蛋了!

wp_verify_nonce() 的源码解析:一层层揭开它的面纱

wp_verify_nonce()函数位于wp-includes/pluggable.php文件中。我们来逐步分析它的源码,看看它是如何工作的。

function wp_verify_nonce( $nonce, $action = -1 ) {
    $nonce = (string) $nonce; // 确保 nonce 是字符串
    $action = (string) $action; // 确保 action 是字符串

    $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS ); // 获取 nonce 的有效时间,默认一天

    $i = wp_nonce_tick(); // 获取 nonce 的 "时间片"

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

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

    // Invalid nonce
    return false;
}

让我们逐行解读:

  1. 类型转换: $nonce = (string) $nonce;$action = (string) $action; 这两行代码确保传入的$nonce$action参数都是字符串类型。这是一个良好的编程习惯,可以避免一些潜在的类型错误。

  2. Nonce 有效期: $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS ); 这行代码获取Nonce的有效期,默认是DAY_IN_SECONDS,也就是一天。你可以通过nonce_life过滤器来修改Nonce的有效期。想象一下,如果你的“暗号”永远有效,那坏人总有一天会猜到!

  3. 时间片: $i = wp_nonce_tick(); 这行代码获取一个叫做“时间片”的东西。这个时间片是用来增加Nonce的安全性的。

    function wp_nonce_tick() {
        $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS );
    
        return ceil( time() / ( $nonce_life / 2 ) );
    }

    wp_nonce_tick() 函数返回一个整数,表示当前的时间片。它将当前时间除以nonce_life / 2(也就是Nonce有效期的一半),然后向上取整。这意味着,Nonce的有效期被分成了两个时间片。

    举个例子:如果Nonce的有效期是24小时,那么每12小时,wp_nonce_tick()的返回值就会加1。这样做的目的是为了让Nonce更难以被预测。

  4. 生成期望的 Nonce:

    $expected = substr( wp_hash( $i . $action, 'nonce' ), -12, 10 );

    这行代码才是Nonce验证的核心。它使用以下步骤生成一个期望的Nonce:

    • 拼接: 将当前时间片$i$action拼接在一起。$action参数是一个字符串,用来标识这个Nonce是用于什么操作的,比如"delete_post"、"edit_post"等等。
    • 哈希: 使用wp_hash()函数对拼接后的字符串进行哈希。wp_hash()函数使用WordPress的盐值(salt)来增加哈希的安全性。盐值是一些随机生成的字符串,存储在wp-config.php文件中。
    • 截取: 从哈希后的字符串中截取最后10位字符作为期望的Nonce。
  5. 比较: if ( hash_equals( $expected, $nonce ) ) { return 1; } 这行代码使用hash_equals()函数来比较用户提交的Nonce和期望的Nonce是否相等。hash_equals()函数是一个安全字符串比较函数,可以防止时序攻击。如果相等,说明Nonce是有效的,函数返回1。

  6. 检查上一个时间片:

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

    这段代码检查用户提交的Nonce是否是上一个时间片生成的。这样做是为了处理一些时钟同步的问题。如果用户的时钟稍微慢了一点,导致他提交的请求的时间片比服务器的时间片慢1,那么这段代码可以保证他的请求仍然有效。

  7. 无效 Nonce: 如果用户提交的Nonce既不是当前时间片生成的,也不是上一个时间片生成的,那么说明Nonce是无效的,函数返回false

wp_create_nonce():生成 Nonce 的幕后英雄

既然要验证Nonce,那就要先生成Nonce。WordPress提供了wp_create_nonce()函数来生成Nonce。

function wp_create_nonce( $action = -1 ) {
    $action = (string) $action;
    $i = wp_nonce_tick();

    return substr( wp_hash( $i . $action, 'nonce' ), -12, 10 );
}

这个函数的代码非常简单,和wp_verify_nonce()函数生成期望的Nonce的代码几乎一样。它也是将当前时间片$i$action拼接在一起,然后使用wp_hash()函数进行哈希,最后截取最后10位字符作为Nonce。

如何在 WordPress 中使用 Nonce?

说了这么多,那么如何在WordPress中使用Nonce呢?

  1. 生成 Nonce: 使用wp_create_nonce()函数生成Nonce。通常在HTML表单中添加一个隐藏的输入框来存储Nonce。

    <input type="hidden" name="my_nonce" value="<?php echo wp_create_nonce( 'my_action' ); ?>">

    这里的my_action$action参数,用来标识这个Nonce是用于什么操作的。

  2. 验证 Nonce: 在处理表单提交的PHP代码中,使用wp_verify_nonce()函数来验证Nonce。

    if ( ! isset( $_POST['my_nonce'] ) || ! wp_verify_nonce( $_POST['my_nonce'], 'my_action' ) ) {
        wp_die( '安全检查失败!' );
    }

    这段代码首先检查$_POST数组中是否存在my_nonce字段,然后使用wp_verify_nonce()函数来验证Nonce。如果验证失败,就使用wp_die()函数来显示一个错误信息。

完整示例:一个简单的表单提交

下面是一个完整的示例,演示如何在WordPress中使用Nonce来保护表单提交:

HTML表单:

<form method="post" action="">
  <label for="my_input">请输入内容:</label>
  <input type="text" id="my_input" name="my_input">
  <input type="hidden" name="my_nonce" value="<?php echo wp_create_nonce( 'my_form_action' ); ?>">
  <input type="submit" value="提交">
</form>

PHP处理代码:

<?php
if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
  if ( ! isset( $_POST['my_nonce'] ) || ! wp_verify_nonce( $_POST['my_nonce'], 'my_form_action' ) ) {
    wp_die( '安全检查失败!' );
  }

  $my_input = sanitize_text_field( $_POST['my_input'] ); // 对用户输入进行安全处理

  echo '<p>你输入的内容是:' . esc_html( $my_input ) . '</p>'; // 显示用户输入
}
?>

在这个示例中,my_form_action$action参数,用来标识这个Nonce是用于保护这个表单的提交。

wp_nonce_url()wp_nonce_field():便捷的 Nonce 工具

除了wp_create_nonce()wp_verify_nonce()函数之外,WordPress还提供了两个便捷的Nonce工具:wp_nonce_url()wp_nonce_field()

  • wp_nonce_url() 这个函数可以在URL中添加Nonce。

    $url = wp_nonce_url( 'admin.php?page=my_page&action=delete', 'delete_my_item' );
    echo '<a href="' . esc_url( $url ) . '">删除</a>';

    这段代码会在URL中添加一个_wpnonce参数,用来存储Nonce。在处理delete操作的PHP代码中,可以使用wp_verify_nonce()函数来验证Nonce。

  • wp_nonce_field() 这个函数可以生成一个包含Nonce的隐藏的输入框。

    wp_nonce_field( 'my_form_action' );

    这段代码会生成以下HTML代码:

    <input type="hidden" id="_wpnonce" name="_wpnonce" value="生成的Nonce值">
    <input type="hidden" name="_wp_http_referer" value="当前页面的URL">

    除了Nonce之外,wp_nonce_field()函数还会生成一个_wp_http_referer字段,用来存储当前页面的URL。这个字段可以用来在验证Nonce之后将用户重定向回之前的页面。

总结:Nonce 是 WordPress 安全的重要组成部分

Nonce是WordPress安全的重要组成部分,它可以有效地防止跨站请求伪造(CSRF)攻击。通过生成和验证Nonce,我们可以确保每一个请求都是由真正的用户发起的,而不是由恶意攻击者伪造的。

函数/方法 作用
wp_create_nonce() 生成 Nonce 值
wp_verify_nonce() 验证 Nonce 值是否有效
wp_nonce_url() 在 URL 中添加 Nonce 参数
wp_nonce_field() 生成包含 Nonce 值的隐藏表单字段,通常用于表单提交,同时包含referer信息
wp_nonce_tick() 返回一个基于时间片的整数,用于 Nonce 的生成和验证,增加安全性。

希望今天的讲座能够帮助大家更好地理解wp_verify_nonce()函数的工作原理,并在实际开发中正确地使用Nonce来保护WordPress网站的安全。 记住,安全无小事,多一份防范,少一份风险!

现在,有没有什么问题呢? 欢迎大家提问!

发表回复

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