WordPress插件安全之随机数验证安全方案

WordPress插件安全之随机数验证安全方案

随机数是“一次性使用的数字”,用于帮助保护 URL 和表单免受某些类型的滥用(恶意或其他)。从技术上讲,WordPress 的 nonce 并非严格意义上的数字;它们是由数字和字母组成的哈希值。它们也并非一次性使用:它们有一个有限的“生命周期”,超过该生命周期就会过期。

在此期间,系统会为特定上下文中的特定用户生成相同的 nonce。该操作的 nonce 将一直保持不变,直到该 nonce 生命周期结束。

WordPress 的安全令牌被称为“nonce”(尽管与真正的 nonce 存在上述区别),因为它们的用途与 nonce 大致相同。它们有助于防御包括 CSRF 在内的多种攻击,但无法防御重放攻击,因为它们不会进行一次性使用检查。切勿依赖 nonce 进行身份验证、授权或访问控制。请使用 保护您的函数current_user_can(),并始终假设 nonce 可能被盗用。

为什么要使用随机数?

要了解为什么使用 nonce,请考虑管理屏幕可能会生成这样的 URL,以丢弃第 123 个帖子。

https://hao-blog.com/wp-admin/post.php?post=123&action=trash

当你访问该 URL 时,WordPress 会验证你的身份验证 Cookie 信息,如果你被允许删除该帖子,WordPress 就会继续删除它。攻击者可以利用这一点,让你的浏览器在你不知情的情况下访问该 URL。例如,攻击者可以在第三方页面上制作一个伪装链接,如下所示:

<img src="https://hao-blog.com/wp-admin/post.php?post=123&action=trash" />

这将触发您的浏览器向 WordPress 发出请求,并且浏览器会自动附加您的身份验证 cookie,而 WordPress 会将此视为有效请求。

添加 nonce 可以避免这种情况。例如,使用 nonce 时,WordPress 为用户生成的 URL 如下所示:

https://hao-blog.com/wp-admin/post.php?post=123&action=trash&_wpnonce=b192fc4204

如果有人试图删除帖子编号 123,而没有 WordPress 生成并提供给用户的正确随机数,WordPress 将向浏览器发送“403 Forbidden”响应。

创建随机数

您可以创建一个随机数并将其添加到 URL 中的查询字符串中,可以将其添加到表单中的隐藏字段中,或者可以以其他方式使用它。

对于要在 AJAX 请求中使用的随机数,通常将随机数添加到隐藏字段,JavaScript 代码可以从中获取它。

请注意,随机数对于当前用户的会话是唯一的,因此如果用户异步登录或退出,页面上的任何随机数都将不再有效。

为访客(未登录用户)自定义随机数

默认情况下,WordPress 核心会为具有相同用户 ID(值 0)的访客生成相同的随机数。也就是说,它无法阻止访客遭受 CSRF 攻击。为了增强关键操作的安全性,您可以为访客开发一个会话机制,并连接到 nonce_user_logged_out 过滤器,以便将用户 ID 值替换 0 为会话机制中的另一个随机 ID。

向 URL 添加随机数

要将随机数添加到 URL,请调用以下命令wp_nonce_url()并指定裸 URL 和表示操作的字符串。例如:

$complete_url = wp_nonce_url( $bare_url, 'trash-post_'.$post->ID );

为了获得最大程度的保护,请确保代表操作的字符串尽可能具体。

默认情况下,wp_nonce_url()会添加一个名为 的字段_wpnonce。您可以在函数调用中指定其他名称。例如:

$complete_url = wp_nonce_url( $bare_url, 'trash-post_'.$post->ID, 'my_nonce' );

向表单添加随机数

要向表单添加 nonce,请调用wp_nonce_field()并指定表示操作的字符串。默认情况下,wp_nonce_field()会生成两个隐藏字段,一个值为 nonce,另一个值为当前 URL(引荐来源网址),并回显结果。例如,以下调用:

wp_nonce_field( 'delete-comment_'.$comment_id );

可能会出现类似这样的结果:

<input type="hidden" id="_wpnonce" name="_wpnonce" value="796c7766b1" />

<input type="hidden" name="_wp_http_referer" value="/wp-admin/edit-comments.php" />

为了获得最大程度的保护,请确保代表操作的字符串尽可能具体。

您可以为 nonce 字段指定不同的名称,也可以指定不需要 referrer 字段,还可以指定只返回结果而不回显。有关语法的详细信息,请参阅:wp_nonce_field()

创建用于其他用途的随机数

要创建用于其他用途的 nonce,请调用wp_create_nonce()并指定表示操作的字符串。例如:

$nonce = wp_create_nonce( 'my-action_'.$post->ID );

这仅返回 nonce 本身。例如:295a686963

为了获得最大程度的保护,请确保代表操作的字符串尽可能具体。

验证随机数

您可以验证在 URL、管理屏幕中的表单、AJAX 请求或其他上下文中传递的随机数。

验证从管理屏幕传递的随机数

要验证在管理屏幕中的 URL 或表单中传递的随机数,请check_admin_referer()调用指定表示操作的字符串。

例如:

check_admin_referer( 'delete-comment_'.$comment_id );

此调用检查随机数和引用者,如果检查失败,则采取正常操作(以“403 Forbidden”响应和错误消息终止脚本执行)。

如果您在创建随机数时没有使用默认字段名称(_wpnonce),请指定字段名称。

例如:

check_admin_referer( 'delete-comment_'.$comment_id, 'my_nonce' );

验证 AJAX 请求中传递的随机数

要验证 AJAX 请求中传递的 nonce,请调用check_ajax_referer() 并指定表示操作的字符串。例如:

check_ajax_referer( 'process-comment' );

此调用检查随机数(但不检查引用者),如果检查失败,则默认终止脚本执行。

如果您在创建 nonce 时未使用默认字段名称(_wpnonce或_ajax_nonce),或者您想执行其他操作而不是终止执行,则可以指定其他参数。有关详情,请参阅:check_ajax_referer()

验证在其他上下文中传递的随机数

要验证在其他上下文中传递的随机数,请调用wp_verify_nonce()指定随机数和表示操作的字符串。

例如:

wp_verify_nonce( $_REQUEST['my_nonce'], 'process-comment'.$comment_id );

如果结果为 false,则停止继续处理请求,而是采取一些适当的措施。通常的做法是调用wp_nonce_ays(),它会向浏览器发送“403 Forbidden”响应。

修改 nonce 系统

您可以通过添加各种操作和过滤器来修改随机数系统。

修改 nonce 生命周期

默认情况下,Nonce 的有效期为一天。超过一天,即使与操作字符串匹配,Nonce 也不再有效。要更改有效期,请添加 nonce_life 过滤器,指定有效期(以秒为单位)。

例如,将生命周期更改为四小时:

add_filter( 'nonce_life', function () { return 4 * HOUR_IN_SECONDS; } );

执行额外验证

check_admin_referrer()当发现 nonce 和 referrer 有效时,要执行额外的验证,请添加一个check_admin_referer操作。

例如:

function wporg_additional_check ( $action, $result ) {

...

}

add_action( 'check_admin_referer', 'wporg_additional_check', 10, 2 );

以同样的方式check_ajax_referer()添加动作。check_ajax_referer

更改错误消息

你可以使用翻译系统更改当 nonce 无效时发送的错误消息。例如:

function my_nonce_message ($translation) {

if ($translation === 'Are you sure you want to do this?') {

return 'No! No! No!';

}

return $translation;

}

add_filter('gettext', 'my_nonce_message');

附加信息

本节包含有关 WordPress 中的 nonce 系统的附加信息,这些信息有时可能会有用。

Nonce 生命周期

请注意,正如 WordPress 的 nonce 并非“一次性使用的数字”一样,nonce 的生命周期并非真正的 nonce 生命周期。WordPress 使用一个包含两个 tick(生命周期的一半)的系统,并会验证当前 tick 和上一个 tick 中的 nonce。在默认设置(24 小时生命周期)下,这意味着 nonce 中的时间信息与自 Unix 纪元以来已过去的 12 小时时间段相关。这意味着在中午到午夜之间生成的 nonce 的生命周期将持续到第二天中午。因此,实际生命周期在 12 到 24 小时之间变化。

当 nonce 有效时,验证 nonce 的函数将返回当前 tick 数,1 或 2。例如,您可以使用此信息来刷新处于第二个 tick 中的 nonce,以使它们不会过期。

随机数安全性

如果您正确安装了 WordPress,则使用您网站独有的密钥和盐来生成 Nonce。NONCE_KEY并NONCE_SALT在您的文件中定义wp-config.php,并且该文件包含提供更多信息的注释。

切勿依赖 Nonce 进行身份验证、授权或访问控制。请使用 保护您的函数current_user_can(),并始终假设 Nonce 可能被盗用。

更换随机数系统

组成 nonce 系统的某些功能是可插入的,因此您可以通过提供自己的功能来替换它们。

要更改验证管理请求或 AJAX 请求的方式,您可以替换check_admin_referrer()或check_ajax_referrer(),或两者。

要用其他 nonce 系统替换该 nonce 系统,您可以替换wp_create_nonce()、wp_verify_nonce()和wp_nonce_tick()。

有关的

随机数函数:wp_nonce_ays(), wp_nonce_field(), wp_nonce_url(), wp_verify_nonce(), wp_create_nonce(), check_admin_referer(), check_ajax_referer(), wp_referer_field()

随机数钩子:nonce_lifenonce_user_logged_out, explain_nonce_(verb)-(noun), check_admin_referer

顶部