Why WordPress Uses Nonces: Understanding CSRF with a Simple Real-World Example

Many WordPress beginners encounter functions like wp_nonce_field() and check_admin_referer() while developing plugins. At first, these functions can seem unnecessary. After all, if only administrators can access your plugin settings, why add another layer of protection?

The answer lies in understanding a common web security attack called Cross-Site Request Forgery (CSRF). In this article, we’ll explore how such an attack works, why it is dangerous, and how WordPress nonces help prevent it.

A Simple Plugin Settings Form

Imagine your plugin has a settings page where an administrator can save an API key.

<form method="post">
    <input type="text" name="api_key">
    <input type="submit" value="Save Settings">
</form>

When the administrator submits the form, the plugin stores the value.

update_option(
    'my_plugin_api_key',
    $_POST['api_key']
);

Everything works perfectly during normal use.

The Administrator Is Already Logged In

When an administrator logs into WordPress, the browser stores a login cookie.

Whenever the browser communicates with the WordPress website, this cookie is automatically included with every request.

This allows WordPress to recognize the administrator without asking them to log in again for every page.

The Unexpected Problem

Now imagine the administrator visits another website while still logged into WordPress.

That website appears harmless, but behind the scenes it contains a hidden HTML form.

<form action="https://mysite.com/wp-admin/admin.php?page=my-plugin"
      method="POST">

    <input type="hidden"
           name="api_key"
           value="HACKED">

</form>

<script>
document.forms[0].submit();
</script>

The administrator never sees this form.

The JavaScript immediately submits it in the background.

What Happens Next?

When the browser submits the hidden form to your WordPress website, it automatically includes the administrator’s login cookie.

From WordPress’s perspective, the request looks completely legitimate because it comes from an authenticated administrator.

If your plugin simply executes:

update_option(
    'my_plugin_api_key',
    $_POST['api_key']
);

the malicious value is saved.

The administrator never clicked your plugin’s Save Settings button. Their browser unknowingly performed the action on their behalf.

Why Is This Dangerous?

At first glance, changing a single setting may not seem like a serious problem. However, many administrative actions can be performed through web forms.

Without CSRF protection, an attacker could trick an administrator’s browser into:

  • Changing plugin or theme settings
  • Publishing unwanted posts or announcements
  • Creating a new administrator account
  • Deleting important data
  • Disabling security plugins
  • Importing malicious configuration files
  • Triggering actions that execute harmful code

The attacker never needs to know the administrator’s password.

Instead, they misuse the administrator’s already authenticated browser.

Enter WordPress Nonces

WordPress solves this problem by adding a nonce to forms.

wp_nonce_field( 'save_settings' );

This generates a hidden field similar to:

<input type="hidden"
       name="_wpnonce"
       value="9f8d72e1ab">

When the form is submitted, your plugin verifies the nonce.

check_admin_referer( 'save_settings' );

If the nonce is missing or invalid, WordPress immediately rejects the request.

Why Can’t the Attacker Guess the Nonce?

The nonce is generated by WordPress specifically for the logged-in user and is embedded in the genuine plugin page.

A malicious website cannot simply invent a valid nonce value.

Without the correct nonce, the forged request fails, even though the administrator is logged in.

Cookies and Nonces Serve Different Purposes

It is important to understand that authentication cookies and nonces solve different problems.

The login cookie answers:

Who is making this request?

The nonce answers:

Did this request originate from a legitimate WordPress form?

Both checks are necessary for secure plugin development.

A Real-World Analogy

Imagine entering your office using your employee ID card.

Once inside, someone hands you a sealed envelope and asks you to place it in the manager’s mailbox.

You assume it’s legitimate and deliver it.

Later, the manager discovers the envelope contains a fake resignation letter or an unauthorized payment request.

The manager trusted the envelope because it was delivered by you, even though you never intended to send that message.

A CSRF attack works in much the same way.

The attacker doesn’t steal your identity. Instead, they trick your browser—which WordPress already trusts—into performing actions on your behalf.

Key Takeaways

  • Being logged into WordPress does not automatically protect against CSRF attacks.
  • A malicious website can cause a logged-in browser to submit unwanted requests.
  • WordPress nonces help verify that a request originated from a genuine WordPress page.
  • Every plugin that processes forms should use wp_nonce_field() when generating the form and check_admin_referer() (or check_ajax_referer() for AJAX requests) before processing submitted data.
  • Authentication confirms who is making the request, while nonces help verify where the request came from.

Understanding this distinction is one of the most important milestones in becoming a secure WordPress plugin developer.

Leave a Reply