DEV Community

David Woolf
David Woolf

Posted on • Originally published at indexforwp.com on

Handling Nonces in WordPress

WordPress provides an easy to use nonce system to create and verify unique hashes for certain actions. Note that WordPress uses the word nonce to refer to a security token but they aren’t true nonces. Here is how WordPress defines their use of nonce:

WordPress’s security tokens are called “nonces” (despite the above-noted differences from true nonces) because they serve much the same purpose as nonces do. They help protect against several types of attacks including CSRF, but do not protect against replay attacks because they aren’t checked for one-time use. Nonces should never be relied on for authentication, authorization, or access control. Protect your functions using current_user_can(), and always assume nonces can be compromised.

While there are plenty of times that using a nonce is not appropriate, some examples of good use cases for nonces are:

  • clicking a link that would alter the database
  • submitting a form that would alter the database
  • using WordPress’s REST API to perform POST, PUT, or DELETE actions

Note: a nonce is valid for 24 hours, and is not a one-time use code. This is why WordPress doesn’t consider their version true nonces.

How to create a nonce

There are a couple of ways to create nonces in WordPress, depending on where you are placing the nonce:

Generate a nonce code by itself

The most basic option is wp_create_nonce which will give you the nonce value. I find this useful for passing a nonce to a REST API endpoint as a header.

$nonce = wp_create_nonce($action); // can be a string or integer

// example:
$nonce = wp_create_nonce('trash-post-23'); 
// outputs: 5817bdf2ac
Enter fullscreen mode Exit fullscreen mode

Generate a url with a nonce parameter

Next, we have wp_nonce_url which takes a base URL, the nonce action name, and the name of the parameter (defaults to _wpnonce). This will return a full URL with the nonce appended as a URL parameter

$url_with_nonce = wp_nonce_url($url, $action, $name);

// example: 
$url_with_nonce = wp_nonce_url('<https://localhost>', 'trash-post-23', '_wpnonce'); 
// outputs: <https://localhost?_wpnonce=5817bdf2ac>
Enter fullscreen mode Exit fullscreen mode

Generate a hidden input with a nonce name and value

Finally, we have wp_nonce_field which is a quick way to output a hidden field with the nonce value. It also will add the referrer URL so you can verify the user came from the previous page and has a valid nonce.

wp_nonce_field($action, $name, $referer, $echo);

// example:
wp_nonce_field('trash-post-23');

// outputs: 
// <input type="hidden" id="_wpnonce" name="_wpnonce" value="5817bdf2ac">
// <input type="hidden" name="_wp_http_referer" value="/wp-admin/plugins.php?plugin_status=all&amp;paged=1&amp;s">
Enter fullscreen mode Exit fullscreen mode

How to verify a nonce

When working inside the WordPress dashboard, you can quickly verify a nonce and referrer by calling check_admin_referer. By default, you need to pass in the action name. If you changed the nonce parameter name (defaults to _wpnonce), you can pass that in as a second parameter

check_admin_referer($action, $name);

// example: 
check_admin_referer('trash-post-23');
Enter fullscreen mode Exit fullscreen mode

When working with async requests, you have a couple of options:

check_ajax_referer

The check_ajax_referer function will handle verification and stop processing the request

check_ajax_referer($action);

// example:
check_ajax_referer('trash-post-23');
Enter fullscreen mode Exit fullscreen mode

wp_verify_nonce

The wp_verify_nonce function will handle verification but it is up to you to return an error. It’s recommended to return a 403: Forbidden error.

wp_verify_nonce($nonce, $action);

// example:
wp_verify_nonce('5817bdf2ac', 'trash-post-23'); // the nonce itself would be passed to the request and verified from the body or parameter list
Enter fullscreen mode Exit fullscreen mode

Using nonces with REST API requests

If you are creating your own API requests in WordPress (for example: with fetch) and you want to submit requests using POST, PUT, or DELETE, then you will have to pass a nonce value. Fortunately, this is pretty easy if you are enqueuing your javascript files correctly.

When using wp_enqueue_script you can pass another function called wp_localize_script with any data you want to be accessible from that file:

wp_enqueue_script(
    'ndx-script',
    $path,
  [],
  '1.0.0',
  true
);

wp_localize_script(
    'ndx-script', 
    'wp_api', 
    [
        'nonce' => wp_create_nonce('wp_rest')
    ]
);

Enter fullscreen mode Exit fullscreen mode

Basically, you reference the name of your enqueued script, set your own variable name to use in javascript, and then pass an array of options. This will then provide you a global object in javascript. Here is how you would use the nonce in a fetch statement

fetch(PATH, {
    headers: {
        'X-WP-Nonce': wp_api.nonce
  },
  method: 'POST',
    body: JSON.stringify({ name: value })
})
.then(response => response.json())
.then(response => {
    // do something here
})
Enter fullscreen mode Exit fullscreen mode

The only important part of this is the headers option in the second parameter of the fetch statement, passing the nonce. Custom headers always start with X and then WP-Nonce is defined by WordPress itself and what they’ll look for.

What’s great about this pattern is that you don’t need to verify the nonce in your rest endpoint. The system takes care of that for you and will return a 403: Forbidden error if you try to submit something without a nonce

Note: publicly consumable API endpoints do not require this which is why you can fetch a list of posts right from your browser in the URL field.

Wrap up (and a note on security)

Once again, it’s important to note that nonces should not be used in place of proper authentication. They are simply an extra check to prevent authenticated users from performing an action they didn’t intend to.

With the REST API especially, the nonce example here is only for requests sent while a user is logged in, and your endpoints should also use the permissions callback to check if the current user can perform the intended action.

I would also recommend you use the nonce functions that take care of some of the heavy lifting for you (like creating and verifying hidden fields with the nonce value).

Author

david_woolf image

Discussion (0)