WordPress nonces are an easy piece of security measure you can implement into your plugins or themes to prevent your users from Cross Site Request Forgery attacks. But how do WordPress nonces really work? You heard they were valid for 24 hours? Are they really? How can they be called nonces if they can be reused?
Let’s dive right into in and see how WordPress nonces are not pure nonces but still are useful to provide an higher level of security to your website’s users.
What is a Nonce?
A nonce simply stands for a Number used ONCE. It’s a unique token used to add a layer of security to your application and also to validate the intent of a user initiated action.
This Nonce is generated by a server-side application, stored on the server and sent to the client to be part of the payload it’s going to send back to the server. This way, you have a way to validate the payload and have a higher level of certainty that the request was actually made by the client.
Why use a Nonce?
A nonce could be seen as a one time password for user initiated actions. May it be sending a form, encrypting data or executing an action, the nonce adds a level of security by preventing a malicious script sending forged requests to your application. This is called Cross Site Request Forgery ( CSRF or XSRF ) and malicious scripts use this method to send requests on your server in a way that you have not intended to. This can have important consequences depending on what your script is used for.
An example of this is an attacker putting up a script on his website that POST
to a form on your website automatically on an authenticated user session without the user really wanting to do that specific action. That was a mouthful! In other words, an attacker disguising a link to your authenticated user doing something (potentially bad) that the user never intended to do.
So if your form doesn’t contain a nonce, then there is not much that prevents an attacker to flood bad data in your database, potentially bringing down your whole site or worse, costing you or your users to lose money or to leak private information. If your form contains a nonce, then the POST
would not go through since the nonce would not be able to be validate positively coming really coming from that user. It would not satisfy the intent part of the request.
This example includes forms as it’s an easy target for CSRF, but really, any requests made to a server that a script is listening for is vulnerable to CSRF if no precaution are taken to prevent it.
Breaking down the process of a nonce, it should look something like this:
- The server generates the unique token ( nonce ), stores it locally and passes it on to the client.
- The client does it’s thing, prepares the payload and includes the nonce in it.
- When the server receives the payload, it first checks if it contains a nonce, then, it will validate it by comparing it to the one stored locally. If a match can be made, the payload is considered genuine. If not, the payload should not be trusted.
- After the validation returned a match, the process should invalidate ( or destroy ) the nonce for it to be a true used ONCE number.
If you properly create your nonce, CSRF should not be a problem. Since a properly formed nonce should contain at least a secret key ( like a secret salt securely stored on your server ) and a unique or at least very descriptive action as a name ( maybe including a timestamp ? ), then there is no way an attacker could recreate a valid nonce without having access to both parameters.
So a true nonce would be destroyed after it’s been validated, effectively preventing an action of being performed twice ( or multiple times ), may it be maliciously or accidentally, without being validated again by a new nonce. A nonce also prevents CSRF attacks since secret salts and other unique parameters are used to created the nonce and are not available to an attacker to replicate a fake nonce.
About WordPress Nonces Implementation
Ok, so now that we explained what a nonce is, let’s see why WordPress nonces are not true nonces.
The way WordPress implements nonces lacks the one part that makes a nonce a nonce, it’s “used ONCE” part.
WordPress creates a nonce that will remain valid for at least 12 hours by default (can be valid for up to 24 hours). This implementation in itself invalidates the definition of a true nonce because a generated nonce can be used an unlimited amount of time, within that valid period. A new one will be generated every 12 hours (UTC time), and will remain valid for a maximum of another 12 hours depending on when it was generated within the tick cycle (See graph below).
Tick tock
This brings me to talk about ticks.
Ticks are what WordPress uses, as one parameter, to create a nonce.
It is generated by the wp_nonce_tick()
function used in the nonce creation process.
So what is a tick? It’s a value that changes a midnight (+1 second) and midday (+1 second) by 1 unit. So every 12 hours (UTC time).
You might have read that a WordPress nonces are valid for 24 hours, but that’s just not true. At least not entirely.
A WordPress nonce is valid for a maximum of 24 hours. Or in truer terms, it is valid for the current tick’s value and the previous one.
But since a tick changes every 12 hours, a nonce will only truly be valid for 24 hours if it’s created at the beginning of the tick, specifically, at hour 0:00:01 or 12:00:01. The more you advance towards the end of the current tick, the shorter the lifespan of the nonce will be (or closer to a lifespan of 12 hours).
As you can see in the graph, a nonce created within the end of a tick, will have a shorter lifespan than a one created towards the beginning. This is because a wp nonce is generated with that tick’s value as one of it’s parameters. If we look at how wp_verify_nonce()
verifies a nonce, we’ll see that it checks the current tick value, and the previous one (tick – 1). If the wp nonce is verified close to 24 hours later and it was created deeper within the cycle of the tick, then the nonce would be 2 ticks behind, failing the verification.
On the graph, only NONCE 1, 13 and 25 will be valid for close to 24 hours, assuming they are generated at the beginning of the hours not the end of it.
I agree that in reality, this should not be that much of an issue, since nonce should be used relatively quickly after their creation to verify actions/requests.
But it’s a misconception thinking that WordPress nonces are valid for 24 hours, when in reality, we should say they are valid for 2 ticks or for a maximum of 24 hours.
And it’s not a good idea to rely on WordPress nonces to validate an action that could take more than 12 hours for a user to actually request. Since the nonce will become invalid anywhere between 12 and 24 hours, chances are that the nonce validation would fail and results in unexpected behaviours
WordPress Nonces functions
Here’s a list of useful WordPress functions to manipulate nonces
Creation functions
Verification functions
wp_verify_nonce()
check_admin_referer()
– name is misleading but still used for backward compatibilitycheck_ajax_referer()
Other functions
WordPress Nonces hooks
Also, here’s some hooks you might find useful
- Filter: nonce_life
- Filter: nonce_user_logged_out
- Action: wp_verify_nonce_failed
- Action: check_admin_referer
- Action: check_ajax_referer
Invalidating WordPress Nonces
Like I discussed above, nonces will auto expire in maximum 24 hours after they’ve been created.
But I prefer to say, they will auto expire 2 ticks from now.
The other way a nonce can become invalid is if a user’s session token changes. That state change occurs when a user logs out and back in again. Since a new session token is generated, all previous nonces won’t validate anymore.
To make things clearer, WordPress doesn’t store the nonce it creates for a user. Instead, it stores the session token and will use it to rehash a nonce upon validation. If the session has not changed and the nonce is still within the 2 ticks valid period, then the submitted nonce and the rehashed one will match. Of course, like I just told you, if either the current tick is 2 or greater than the one used to create the nonce or the session token changes, then the nonce is invalid.
Security Concerns
WordPress not offering true nonces doesn’t mean you should not implement it within your application. It’s still useful to prevent CSRF.
But you should not rely on nonces to securely allow actions that should be restricted to specific users or users roles within your WordPress installation.
Your scripts should only allow such actions for users with the right capabilities (permissions) to do so.
Here, your best tool to use is current_user_can()
and wrap your functions within this check.
Remember that nonces will tell you that the payload is from a genuine source (user), but it does not tell you if that source has sufficient permission to actually execute the payload.
This should be taken care of by another process, thus checking against capabilities.
Using WordPress Nonces
Ok, let’s give you a few example to show you how easy it is to implement a nonce in your code.
Like I listed above, there are 3 functions available to create a nonce wp_create_nonce()
, wp_nonce_field()
and wp_nonce_url()
.
wp_create_nonce()
The first one is a general usage function, meaning you can use the created nonce anywhere you would like.
The function takes 1 parameter, the $action
name (optional) and returns the nonce for you to use.
While the action name is optional, it is recommended to supply one as specific to the action as possible. This will prevent a user having the same nonce for 2 different actions. For instance, deleting a custom post type and sending an email.
So let’s say I have a plugin that sends email to my users. The user I want to send an email to is stored in $send_to_id
. I will then pass that user id value in the request so I can used again when I’ll be verifying the nonce.
Then to create a unique action nonce I would simply do it like this
$send_nonce = wp_create_nonce( 'send_email_user_' . $send_to_id );
This could then be used within an URL like this
<a href="send_mail.php?action=send_user_email&nonce_token=<?php echo esc_attr( $send_nonce ); ?>&user=<?php echo esc_attr( $send_to_id ); ?>"><?php esc_html_e( 'Send Email to User', 'textdomain' ); ?></a>
wp_nonce_field()
This function will output or return the nonce and markup for an hidden form input.
This function takes 4 optional parameters. In order with their default values: $action = -1
, $name = "_wpnonce"
, $referer = true
and $echo = true
Again, while optional, it is recommended to provide better security, to set a custom and specific $action
value and a custom $name
for the field. The value of $name
will be the name of the field what you will use to retrieve the value of the created nonce in your request ( $_REQUEST
, $_POST
or $_GET
).
The $referer
parameter outputs a second hidden field with the name _wp_http_referer
containing the value of the referrer URL as found in the 'REQUEST_URI'
element of the $_SERVER
PHP superglobal variable, unless it is set to false
.
Finally, the wp_nonce_fields()
function prints by default the field, but if you set $echo
to false
the function will return it so you can use it within PHP.
So, reusing the same sending email example as above, creating the nonce and outputting the nonce and referrer fields using this function is as simple as
<form method="post">
<!-- some form inputs here ... -->
<input type=hidden name="user" value="<?php echo esc_attr( $send_to_id ); ?>">
<?php wp_nonce_field( 'send_email_user_' . $send_to_id, 'nonce_token' ); ?>
</form>
wp_nonce_url()
This third function provided by WordPress to create a nonce, outputs an escaped and formed URL containing your nonce.
This functions takes in 3 parameters. In order, with their default value: $actionurl
, $action = -1
and $name = "_wpnonce"
.
$actionurl
is required and is the URL to which you should add your nonce. Here also, providing a descriptive and unique action name and custom name for the query string is optional, but recommended.
Using again the scenario above: sending an email to a user. Here’s what it would look like using wp_nonce_url()
<?php
$url = wp_nonce_url( admin_url(), 'send_email_user_' . $send_to_id, 'nonce_token' );
$url = add_query_arg( 'user', $send_to_id, $url ); // Add the id of the user we send to
?>
<a href="<?php echo $url; ?>"><?php esc_html_e( 'Send Email to User', 'textdomain' ); ?></a>
wp_verify_nonce()
Once you have created your nonce, and used it, you need a way to verify it. Your general goto function to complete this will be wp_verify_nonce()
.
This function takes in 2 parameters, the first one, $nonce
is required and is the actual value of the nonce. The second one, $action
is optional, but you will need to provide it if you used an action name when creating your nonce. Remember that action names are recommended practice.
So let’s see how to use the function, still using our send email example.
$nonce = $_REQUEST[ 'nonce_token' ];
$send_to_id = $_REQUEST[ 'user' ];
if ( ! wp_verify_nonce( $nonce, 'send_email_user_' . $send_to_id ) ) {
die( __( 'Security check', 'textdomain' ) );
} else {
// Nonce validated. Do stuff here.
}
Since wp_verify_nonce()
will return 1
if the nonce was generated within the current tick value and 2
if the nonce was generated in the previous tick value. Then you could do different stuff based on when the nonce was generated.
$nonce = $_REQUEST[ 'nonce_token' ];
$send_to_id = $_REQUEST[ 'user' ];
$nonce = wp_verify_nonce( $nonce, 'send_email_user_' . $send_to_id );
switch ( $nonce ) {
case 1:
echo 'Nonce was generated in the current tick';
break;
case 2:
echo 'Nonce was generated in the previous tick';
break;
default:
exit( 'Nonce is invalid' );
}
check_admin_referer()
Another function you can use to verify a nonce is check_admin_referer()
. While the name is improper and may be misleading, it is still kept for backward compatibility. Also, know that using this function without providing an $action
is obsolete since version 3.2
.
So basically, this function now behaves the same as wp_verify_nonce()
except it will die calling the wp_nonce_ays()
function which display the Are you sure? message.
Example of usage
$nonce = $_REQUEST[ 'nonce_token' ];
$send_to_id = $_REQUEST[ 'user' ];
if ( check_admin_referer( $nonce, 'send_email_user_' . $send_to_id ) ) {
// Nonce validated. Do stuff here.
}
check_ajax_referer()
A last way to verify a nonce would be using the check_ajax_referer()
function.
As the name of the function implies, this last validation function is best used within AJAX request to prevent processing request external to the blog.
The function takes 3 parameters. They are with they default values: $action = -1
, $query_arg = false
and $die = true
.
The to verify the action, you call check_ajax_referer()
hooking into the dynamic wp AJAX hook endpoint.
<?php
//Set Your Nonce
$ajax_nonce = wp_create_nonce( 'send_email_user_' . $send_to_id );
?>
<script type="text/javascript">
jQuery(document).ready(function($){
var data = {
action: 'bn_ajax_send_email_user',
nonce_token: '<?php echo $ajax_nonce; ?>',
user: '<?php echo $send_to_id; ?>'
};
$.post(ajaxurl, data, function(response) {
alert("Response: " + response);
});
});
</script>
then you hook a custom function to the action defined in your javascript AJAX definition.
add_action( 'wp_ajax_bn_ajax_send_email_user', 'bn_ajax_send_email_user_callback' );
function bn_ajax_send_email_user_callback() {
if ( check_ajax_referer( 'wpdocs-special-string', 'nonce_token' ) ) {
bn_send_email( $_REQUEST[ 'user' ] );
wp_die(); // Need to exit the AJAX callback
}
}
WordPress True Nonce
While implementing your nonce methods could be relatively easy to implement, why reinvent the wheel when you could start of by using what’s done already!
There’s a repo on github called wp-simple-nonce that works just fine and will bring you true nonce for you to implement in your application.
While WordPress nonces implementation is good to prevent CSRF, it is not very useful to prevent multiple submission of the same data, which is often a reason to implement nonces within your application. This is why you might want to take a look at WP Simple Nonce
Wrap up
I hope you found this post constructive, if you have questions, comments or concerns, let me know by commenting below!
Cheers!
[14]:
ammy says
Thanks for the great article! if a want to generate a new page (new URL) should I use a nonce? I think I should but the nonce returns false if I’m not logged in. My purpose is others can view the new URL without logging in. Thanks a lot in advance!
nicolas says
Hello Ammy,
Thanks for the kind words!
I’m not sure I understand your use case, but let me say this.
WP NONCES are mainly used to protect (logged in) users from CSRF by providing the user with an unique link on (more critical) actions to prevent a man in the middle attack (i.e. replacing a link with a fraudulent one). An attacker wouldn’t be able to recreate the nonce without knowing the WordPress salt.
If you need some kind of protection for visitors of your site, then WP nonces are not made for that. You could try real nonces maybe (https://github.com/wahabmirjan/wp-simple-nonce)?
Cheers
ammy says
Hi,
Thanks for your fast respond,
What I’m trying to do is to create a new page with a new link (it will expire when I decide, but I’m taking care of that), so so far I think I don’t need any nonce, right?!
The problem is I’m getting a warning: Processing form data without nonce verification
nicolas says
Well are what kind of form are you POSTing? You are using WP nonces for logged in users right? Again, I can’t be sure for your specific use case but you might need to use WP nonces to protect your users.
So maybe your false validation comes from you using the wrong parameters to validate the nonces.
Double check that to make sure you are validating against the correct nonces parameters
ammy says
I’m sure I’m using the right parameters, I’m checking them with a different case and it’s working well.
I’ll try to be more specific:
I need my URL that I’m creating to be shown everywhere, also when users are not logged in, and for many users at once.
What I’m checking is That the URL is not expired and so on… these parameters, that I want to check are passed in the URL so my code uses $_GET to check it, and this is where the warning comes from, on the other hand, if I use nonce if seems to block some users.
Was I clear? Does this make sense?
nicolas says
Ok, it’s making more sense yes.
If you need to validate URL that is time sensitive, then I would suggest you implement a custom validation mechanism. What you want is a validation key and Nonces by definition are ment to be used once. So as I understand it, your link will be valid until your key is valid, once you invalidate your key, the link cannot be used anymore. So you would need to generate a unique key and store it in a table, then write some validation code to check the link against the key within your table.
Again, this would be custom made for your use case.
Hope this helps a bit more 🙂
ammy says
when I add a nonce to a link and use wp_verify_nonce I’m checking if the user can have access the link?
What if I want everyone to have access?
nicolas says
Actually, not really.
When you create a wp_nonce, you tell the user that the link comes from the current site (because it’s hashed using the SALT found in wp-config.php). If a malicious script would try to access the url, it wouldn’t know the salt and couldn’t recreate a valid WPnonce.
As I said, if you wish to give access to a url based on a valid timeframe, you would need to code something specific for your use case and generate some unique key that would be stored in your database.
Then you could invalidate a key after
n
hours pretty easily and this would allow you to share the link with anyone without a problem