WordPress前端安全:CSRF和Nonce

Avatar of Andy Adams
Andy Adams 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

在我们 上一篇文章 中,我们介绍了跨站脚本 (XSS) 以及 WordPress 提供的防止 XSS 攻击的功能。今天,我们将探讨前端开发人员面临的另一个安全问题:跨站请求伪造 (CSRF)。

不要以为这些安全问题不重要,最近在 WP SEO 插件中发现了一个重大漏洞,该插件已安装在 1,000,000 多个 WordPress 网站上,并且允许黑客使用 CSRF 操纵 WordPress 数据库。(该插件已迅速修复,但您可以看出这类问题有多可怕。)

Nonce和跨站请求伪造

简单来说,CSRF 是指坏人试图欺骗用户(通常是具有 WordPress 仪表板访问权限的用户)执行他们本意不希望执行的操作。一个简单的例子可以帮助说明。

假设您正在构建一个 WordPress 插件,允许登录的用户提交他们的图片。在网站的前端,您的插件可能会生成如下表单

<?php if ( is_user_logged_in() ) : ?>
  <form action="/submit-picture/" method="get">
    <input type="text" name="picture_url">
    <input type="submit">
  </form>
<?php endif; ?>

在后端,您处理图片表单提交

if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {
  // Save the picture here
}

现在,假设一个黑客想要提交一张伪造的图片。黑客没有用户名或密码——所以他们什么也做不了,对吧?

不完全是。为了劫持您的其中一个用户的帐户以提交伪造的图片,黑客可能会尝试让登录的用户点击如下所示的链接

http://your-site.com/submit-picture/?picture_url=fake-picture.jpg

如果登录的用户点击该链接,是什么阻止了图片被提交?您猜对了:**什么也没有**。黑客获胜。

这是因为我们没有做任何事情来验证用户提交图片的**意图**。“F”在 CSRF 中代表伪造:黑客代表用户伪造(伪造)了一个请求,有点像您在小学时伪造妈妈的签名在病假条上一样。

如何防止CSRF

我们可以通过使用 WordPress 中内置的一些便捷功能来阻止 CSRF 攻击。为了防止请求被成功“伪造”,WordPress 使用 Nonce(一次性数字)来验证请求是否确实由当前用户发出。

基本流程如下

  1. 生成一个Nonce。
  2. 该Nonce与表单一起提交。
  3. 在后端,检查Nonce的有效性。如果有效,则继续操作。如果无效,则所有操作都将停止——请求可能被伪造了!

让我们添加一个Nonce

在前端,假设我们想为表单提交添加一个Nonce。我们将使用 wp_nonce_field 便利函数来实现

<form action="/submit-picture/" method="get">
  <input type="text" name="picture_url">
  <input type="submit">
  <?php wp_nonce_field( 'submit_picture' ); ?>
</form>

很简单,对吧?现在,当我们在后端处理此表单时,我们只需要使用一些 WordPress 内置函数来验证Nonce是否有效

// By default, we can find the nonce in the "_wpnonce" request parameter.
$nonce = $_REQUEST['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'submit_picture' ) ) {
  exit; // Get out of here, the nonce is rotten!
}

// Now we can process the submission
if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {
  // Save the picture here
}

由于黑客不知道Nonce的值,因此他无法创建会导致危害的伪造链接。任何恶意尝试都将在wp_verify_nonce 检查时被阻止。我们阻止了黑客,对吧?!在许多情况下,是的。我们让黑客伪造请求变得更加困难。

Nonce是特定于用户的

但是,如果您的网站的一个恶意登录用户想要为另一个用户提交伪造的图片呢?恶意用户能否只是查看网站的 HTML 源代码,找到 Nonce 字段,并将其添加到他们的“恶意”URL 中,如下所示?

http://your-site.com/submit-picture/?picture_url=fake-picture.jpg&_wpnonce=NONCEVALUE

幸运的是,它不会起作用。WordPress Nonce是 特定于用户会话的,因此黑客无法用自己的Nonce替换另一个用户的Nonce。

感谢评论中 Mark Allen 指出特定于会话的Nonce。

尝试这些Nonce函数

WordPress 充满了便捷函数,使生成 Nonce(和防止 CSRF)变得更容易。以下是一些您可能会在不同情况下发现有用的函数

函数:wp_verify_nonce

功能:验证Nonce值是否由 WordPress 正确生成。

无论您如何生成 Nonce,都应在后端使用wp_verify_nonce 进行检查

就像上面的示例一样,使用wp_verify_nonce 验证用户的意图

$submitted_value = $_REQUEST['_wpnonce'];

if ( wp_verify_nonce( $submitted_value, 'your_action_name' ) ) {
  // nonce was valid...
}

wp_verify_nonce 的 Codex 条目

函数:wp_nonce_field

功能:打印包含 Nonce 值的隐藏<input> 标签。

用于构建需要防止 CSRF 的<form>(几乎每个<form> 都需要)。

就像上面的示例一样,wp_nonce_field 是一个便捷函数,使我们构建 HTML 表单变得更容易

<form>
<!-- Other form fields go here -->
<?php wp_nonce_field( 'your_action_name' ); ?>
</form>

wp_nonce_field 的 Codex 条目

函数:wp_nonce_url

功能:返回附加了 Nonce 值的 URL。

用于构建需要将 Nonce 字段作为查询字符串参数附加的 URL。在需要 CSRF 验证的 GET 请求中非常有用。

这是一个wp_nonce_url 的示例,其中我们需要验证用户是否确实希望点击链接

<?php
  $action_url = wp_nonce_url( '/change-color/?color=blue', 'change_color' );
  // $action_url is now "/change-color/?color=blue&_wpnonce=GENERATED_VALUE"
?>
<a href="<?php echo esc_url( $action_url ); ?>">Change to blue</a>

wp_nonce_url 的 Codex 条目

函数:wp_create_nonce

功能:生成 Nonce 值。

用于需要不适合上述便捷函数的 Nonce 值的情况。

以下是如何使用wp_create_nonce 生成以 JSON 格式返回的 Nonce 值(在 AJAX 请求中返回时很有用)

<?php
  $nonce_value = wp_create_nonce( 'your_action_name' );

  return json_encode( array( 'nonce' => $nonce_value ) );
?>

wp_create_nonce 的 Codex 条目

函数:check_ajax_referer

功能:使用wp_verify_nonce 检查提交的 Nonce,如果验证失败,则停止执行。

check_ajax_referer 是另一个便捷函数,主要用于 AJAX 请求。您可以使用它来跳过自己执行wp_verify_nonce 检查

<?php
  // Forged requests won't get past this line:
  check_ajax_referer( 'your_action_name' );

  // Do your action here
?>

check_ajax_referer 的 Codex 条目

所有内容都使用Nonce!

CSRF 漏洞的危害程度从无害(例如投票提交)到极其危险(例如修改密码或权限)不等。

无论严重程度如何,**只要用户在 WordPress 中执行操作**,都应该使用 nonce 来防止 CSRF 攻击。因为……为什么不呢?你肯定不希望黑客伪造你网站上的请求,而 WordPress 提供了阻止他们的工具。

使用 nonce,阻止伪造,挫败黑客!