What Are WordPress Nonces?

Tim Carr
Tim Carr
Share

Keeping your WordPress theme or plugin code secure is important to prevent possible attacks from malicious users.

We’ve previously covered how to sanitize, escape and validate form data in WordPress, as well as improving your WordPress theme quality with the VIP Scanner.

Today we’ll look at how nonces (number used once) can also help keep your WordPress themes and plugins secure.

What Are WordPress Nonces?

WordPress nonces are defined as:

… a “number used once” to help protect URLs and forms from certain types of misuse, malicious or otherwise.
https://codex.wordpress.org/WordPress_Nonces

Whilst in WordPress a nonce isn’t technically a number (it’s a hash made up of letters and numbers), it does help prevent actions from being run by malicious users.

WordPress nonces work in two parts:

  • Creating a nonce (hash), submitting it via a form or action, and
  • Verifying a nonce, before accepting form data or running an action.

For example, when you go to delete a post in the WordPress Administration Screen, you’ll notice the URL contains a _wpnonce parameter:
http://127.0.0.1/tuts/wp-admin/post.php?post=542&action=trash&_wpnonce=a03ac85772

The routine, which deletes a post, will check that post 542 has a nonce value of a03ac85772 before deleting the post. If the nonce does not exist, or does not match the expected value, the post will not be deleted.

This prevents a malicious user potentially deleting lots of posts. For example, the following wouldn’t work, as the nonce belongs to post ID 542: http://127.0.0.1/tuts/wp-admin/post.php?post=642&action=trash&_wpnonce=a03ac85772
http://127.0.0.1/tuts/wp-admin/post.php?post=742&action=trash&_wpnonce=a03ac85772
http://127.0.0.1/tuts/wp-admin/post.php?post=842&action=trash&_wpnonce=a03ac85772

Are you sure you want to do this

Let’s now look at how we can implement WordPress nonces in a plugin

Setting up Our WordPress Plugin

Let’s start with a basic plugin that has its own settings screen. The settings screen has a field, which can be submitted and saved in the WordPress options table.

Enter the below code in a new file at wp-content/plugins/implementing-wordpress-nonces/implementing-wordpress-nonces.php:

< ?php
/**
* Plugin Name: Implementing WordPress Nonces
* Plugin URI: http://www.sitepoint.com
* Version: 1.0
* Author: n7 Studios
* Author URI: http://www.n7studios.co.uk
* Description: Example Plugin demonstrating how to implement WordPress Nonces
* License: GPL2
*/

class ImplementingWPNonces {

	function __construct() {

		// Plugin Details
        $this->plugin = new stdClass;
        $this->plugin->name         = 'implementing-wordpress-nonces'; // Plugin Folder
        $this->plugin->displayName  = 'Nonces'; // Plugin Name

		add_action( 'admin_menu', array( &$this, 'admin_menu' ) );

	}

	/**
    * Register the plugin settings panel
    *
    * @since 1.0.0
    */
    function admin_menu() {

        add_menu_page( $this->plugin->displayName, $this->plugin->displayName, 'manage_options', $this->plugin->name, array( &$this, 'admin_screen' ), 'dashicons-admin-network' );

    }

    /**
    * Output the Administration Screens
    * Save POSTed data from the Administration Panel into a WordPress option
    *
    * @since 1.0.0
    */
    function admin_screen() {

        // Save Settings
        if ( isset( $_REQUEST['submit'] ) ) {
        	update_option( 'implementing_wordpress_nonces', sanitize_text_field( $_REQUEST[ 'implementing_wordpress_nonces' ] ) );
        	$message = __( 'Settings saved', $this->plugin->name );
        }

        // Output form
        ?>
        

< ?php echo $this->plugin->displayName; ?>

< ?php if ( isset( $message ) ) { ?>

< ?php echo $message; ?>

< ?php } ?>
< ?php } } $implementing_wordpress_nonces = new ImplementingWPNonces;

Activate the plugin via the WordPress Administration > Plugins screen, and you’ll see a new Nonces menu item displayed:
Nonces

Click this, and you’ll be taken to the settings screen with a single field:
Nonces

Enter any value, click Save, and if everything worked, you’ll see confirmation, along with the value you’ve just entered:
Nonces

Demonstrating the Security Flaw

Enter the following URL into your web browser’s address bar (replacing the domain with where you have WordPress installed):
http://127.0.0.1/tuts/wp-admin/admin.php?page=implementing-wordpress-nonces&implementing_wordpress_nonces=abc

Notice what happened? The value was saved as abc, simply by directly accessing a URL and being logged into WordPress:
Nonces

Whilst we could use $_POST instead of $_REQUEST in our code (we’ve used $_REQUEST to make it easier to demonstrate the security issue), this wouldn’t help – a malicious user could still, either by themselves or by tricking you into clicking a link – get you to send a POST request to this screen, resulting in the option value being changed.

This is known as a Cross-Site Request Forgery (or CSRF). It’s where a malicious web site, email, application, etc. causes the user’s web browser to perform an unwanted action.

We’ll now create and verify a WordPress nonce, to prevent this attack from being possible.

Securing Our Plugin with a Nonce

As mentioned earlier, there are two steps to the process: first, we need to create a nonce that will be submitted with our form. Second, we need to verify that nonce when the form is submitted.

To create a nonce field in our form, we can use wp_nonce_field():

Retrieves or displays the nonce hidden form field… used to validate that the contents of the form request came from the current site and not somewhere else…

Add the following code just above our input button:

wp_nonce_field( 'implementing_wordpress_nonces_save', 'implementing_wordpress_nonces_nonce' );

wp_nonce_field accepts four arguments – the first two are most important:

  • $action: This determines the specific action we’re running, and should be unique. It’s good practice to prefix your action with the plugin name, as potentially several actions may run. In this case, we’re saving something, so we’ve used implementing_wordpress_nonces_save
  • $name: This determines the name of the hidden field created by this function. As above, we’ve prefixed this with our plugin name, therefore calling it implementing_wordpress_nonces_nonce

If we reload the settings screen, change our value and click Save, you’ll notice the value still changes. We now need to implement a check for the nonce field that was submitted, using wp_verify_nonce( $name, $action ):

Verify that a nonce is correct and unexpired with the respect to a specified action. The function is used to verify the nonce sent in the current request usually accessed by the $_REQUEST PHP variable.

Replace the Save Settings section of our plugin’s admin_screen() function with code the below:

// Save Settings
if ( isset( $_REQUEST['implementing_wordpress_nonces'] ) ) {
	if ( isset( $_REQUEST[ 'implementing_wordpress_nonces_nonce' ] ) && wp_verify_nonce( $_REQUEST[ 'implementing_wordpress_nonces_nonce' ], 'implementing_wordpress_nonces_save' ) ) {
    	update_option( 'implementing_wordpress_nonces', sanitize_text_field( $_REQUEST[ 'implementing_wordpress_nonces' ] ) );
    	$message = __( 'Settings saved', $this->plugin->name );
    } else {
    	// Nonce could not be verified - bail
    	wp_die( __( 'Invalid nonce specified', $this->plugin->name ), __( 'Error', $this->plugin->name ), array(
    		'response' 	=> 403,
    		'back_link' => 'admin.php?page=' . $this->plugin->name,
    	) );
	}
}

This code performs a few actions:

  • First, it checks that we’ve submitted something.
  • Then, it checks that our nonce field exists, and if so attempts to verify the nonce’s value against the action we’re expecting.
  • If the checks pass, the option is updated.
  • If the checks fail, we throw a 403 error with the message “Invalid nonce specified”.

To ensure our nonces are being created and validated, let’s try to access our ‘malicious’ direct URL again:
http://127.0.0.1/tuts/wp-admin/admin.php?page=implementing-wordpress-nonces&implementing_wordpress_nonces=abc.

If our nonces are implemented and being verified, you’ll see the Invalid nonce specified notice:
Nonces

Using Nonces in AJAX Requests

Say we want to save changes via an AJAX call, rather than reload the entire screen. We can do this with a few code tweaks.

First, let’s load some JavaScript into our plugin, and register an AJAX handler, by adding the following to our __construct():

add_action( 'admin_enqueue_scripts', array( &$this, 'admin_scripts_css' ) );
add_action( 'wp_ajax_implementing_wp_nonces', array( &$this, 'admin_ajax_save' )  );

In our class, add the corresponding admin_scripts_css function:

/**
* Register and enqueue any JS and CSS for the WordPress Administration
*
* @since 1.0.0
*/
function admin_scripts_css() {

	// JS
	wp_enqueue_script( $this->plugin->name, plugin_dir_url( __FILE__ ) . 'admin.js', array( 'jquery' ), '1.0', true );

}

For our AJAX call, add the corresponding admin_ajax_save function:

/**
 * Saves POSTed settings data
 *
 * @since 1.0.0
 */
function admin_ajax_save() {

	// Save option and return 1
	update_option( 'implementing_wordpress_nonces', sanitize_text_field( $_REQUEST[ 'implementing_wordpress_nonces' ] ) );
	echo 1;
	die();

}

Finally, we need to create a new file in our plugin folder for our JavaScript routine, which will POST the data via AJAX when the form is submitted. Let’s do this by creating a new file called admin.js, inserting the following:

jQuery( document ).ready( function( $ ) {

	$( 'form#implementing-wordpress-nonces' ).submit( function( e ) {

		// Prevent form submission
		e.preventDefault();

		// Submit form via AJAX
		$.post(
	        ajaxurl, // Set by WordPress
	        {
	        	'action': 'implementing_wp_nonces',
	        	'implementing_wordpress_nonces': $( 'input#implementing_wordpress_nonces' ).val()
	        },
	        function(response) {
	            if ( response ) {
	            	alert( 'Settings Saved' );
	            }
	        }
	    );
    });

} );

Reload our settings screen, and if everything worked, when you submit the form, you’ll get an on screen confirmation to say that the settings saved.
Nonces

We can reload the settings screen again to make sure the value did update successfully:
Nonces

Again, we now need to implement both the creation and validation of a WordPress nonce, to ensure that CSRF attacks can’t happen via an AJAX request.

For creating nonces for AJAX requests, we can use the WordPress wp_create_nonce function:

Generates and returns a nonce. The nonce is generated based on the current time, the $action argument, and the current user ID.

It accepts a single $action argument, so we’d use:

$nonce = wp_create_nonce( 'implementing_wordpress_nonces_ajax_save' );

To get this nonce hash into JavaScript’s scope, we can use wp_localize_script() to send a JS object. Let’s add the following code below the wp_enqueue_script call in our plugin:

wp_localize_script( $this->plugin->name, 'implementing_wordpress_nonces', array(
	'nonce' => wp_create_nonce( 'implementing_wordpress_nonces_ajax_save' ),
) );

We also need to update our JavaScript file to send this nonce value with the AJAX POST request:

jQuery( document ).ready( function( $ ) {

	$( 'form#implementing-wordpress-nonces' ).submit( function( e ) {

		// Prevent form submission
		e.preventDefault();

		// Submit form via AJAX
		$.post(
	        ajaxurl, // Set by WordPress
	        {
	        	'action': 'implementing_wp_nonces',
	        	'nonce':  implementing_wordpress_nonces.nonce,
	        	'implementing_wordpress_nonces': $( 'input#implementing_wordpress_nonces' ).val()
	        },
	        function(response) {
	        	if ( response == 1 ) {
	            	alert( 'Settings Saved' );
	            } else {
	            	alert( 'Invalid nonce specified' );
	            }
	        }
	    );
    });

} );

The JavaScript file now sends a $_POST variable called nonce with the value of wp_create_nonce.

Finally, we need to verify the nonce when it’s sent in the AJAX request. Update our admin_ajax_save function:

/**
 * Saves POSTed settings data
 *
 * @since 1.0.0
 */
function admin_ajax_save() {

	// Run a security check first.
	check_ajax_referer( 'implementing_wordpress_nonces_ajax_save', 'nonce' );

	// Save option and return 1
	update_option( 'implementing_wordpress_nonces', sanitize_text_field( $_REQUEST[ 'implementing_wordpress_nonces' ] ) );
	echo 1;
	die();

}

This uses check_ajax_referer, which checks if the given $action (implementing_wordpress_nonces_ajax_save) exists for the given POST field $name (nonce).

If it fails, -1 will be returned, triggering our JavaScript file to show an alert:
Nonces

If it succeeds, 1 will be returned, triggering our JavaScript file to show a success message:
Nonces

Conclusion

We’ve learnt what a WordPress nonce is, and how WordPress uses nonces to prevent malicious CSRF attacks. By building a basic WordPress plugin, we’ve shown how a plugin – and WordPress – can potentially be exploited if nonces are not created and verified.

Using wp_nonce_field() and wp_verify_nonce, we’ve shown how to validate nonces to prevent CSRF attacks. Finally, we implemented AJAX saving of POST data, and used wp_create_nonce and check_ajax_referer to ensure our AJAX requests are also as secure as possible.

For the full source code, check out the GitHub repository or download the code directly.

Frequently Asked Questions about WordPress Nonces

What is the main purpose of WordPress nonces?

The primary purpose of WordPress nonces is to enhance the security of your WordPress website. Nonces, which stands for “Numbers Used Once,” are unique security tokens used by WordPress to validate that the user’s request is legitimate and not a malicious attempt. They are used to protect against several types of attacks, including cross-site request forgery (CSRF), where an attacker tricks a user into performing an action they didn’t intend to.

How does a WordPress nonce work?

A WordPress nonce works by generating a unique token for each user session, form submission, or AJAX request. This token is then verified when the request is processed. If the token doesn’t match, the request is rejected, preventing unauthorized actions. The nonce is a one-time-use token, meaning it changes with every page load or request, making it difficult for attackers to predict.

How can I create a nonce in WordPress?

Creating a nonce in WordPress is straightforward. You can use the WordPress function wp_create_nonce(). This function takes a single argument, a string representing the action you want to secure, and returns a unique token. Here’s an example:

$nonce = wp_create_nonce('my_action');

How do I verify a nonce in WordPress?

To verify a nonce in WordPress, you can use the wp_verify_nonce() function. This function takes two arguments: the nonce you want to verify and the action associated with it. It returns true if the nonce is valid and false otherwise. Here’s an example:

if (wp_verify_nonce($nonce, 'my_action')) {
// The nonce is valid, proceed with the action.
} else {
// The nonce is not valid, stop the action.
}

Can a nonce be used more than once?

Despite the name “Numbers Used Once,” a WordPress nonce can actually be used multiple times within its lifespan, which is typically 24 hours. However, the nonce changes with every page load or request, making it difficult for an attacker to reuse it.

What happens when a nonce expires?

When a nonce expires, it is no longer valid and cannot be used to validate a request. Any attempt to use an expired nonce will result in the request being rejected. This is why it’s important to ensure that your nonces are refreshed regularly, especially for long-running sessions or forms.

How can I refresh a nonce in WordPress?

To refresh a nonce in WordPress, you simply need to create a new one using the wp_create_nonce() function. This will generate a new unique token for the specified action, effectively “refreshing” the nonce.

Can I use nonces for AJAX requests?

Yes, you can use nonces for AJAX requests in WordPress. In fact, it’s highly recommended to do so to protect against CSRF attacks. You can create a nonce as usual with wp_create_nonce() and then pass it to your AJAX script using wp_localize_script().

Are nonces user-specific?

Yes, nonces in WordPress are user-specific. This means that a nonce created for one user will not be valid for another user. This adds an extra layer of security, as it ensures that even if an attacker manages to obtain a nonce, they cannot use it unless they are also logged in as the same user.

Can nonces protect against all types of attacks?

While nonces are a powerful security tool, they are not a silver bullet. They are primarily designed to protect against CSRF attacks, but they cannot protect against other types of attacks such as SQL injection or XSS attacks. Therefore, it’s important to implement other security measures in addition to nonces, such as data sanitization and proper user permissions.