In WordPress there’s a nonce generating/verifying mechanism, and I’m having trouble understanding why it’s not working correctly…
At a point it checks for whether the nonce was generated 0-12 hours ago.
wp-includespluggable.php:1260
function wp_verify_nonce($nonce, $action = -1) {
$user = wp_get_current_user();
$uid = (int) $user->ID;
if ( ! $uid )
$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
$i = wp_nonce_tick();
// Nonce generated 0-12 hours ago
if ( substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10) == $nonce )
return 1;
// Nonce generated 12-24 hours ago
if ( substr(wp_hash(($i - 1) . $action . $uid, 'nonce'), -12, 10) == $nonce )
return 2;
// Invalid nonce
return false;
}
How does that if
work?
// Nonce generated 0-12 hours ago
if ( substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10) == $nonce )
I see that
wp_hash($i . $action . $uid, 'nonce')
=8ae70558afac4a6951e3bc0f9ef1f59a
and $nonce
=3551
. How is $nonce
being compared to -12,10
th part of 8ae70558afac4a6951e3bc0f9ef1f59a
supposed to be a check for “0-12 hours ago”?
WordPress nonces are not your usually (‘use only once’) nonce. For a given
$action
, a new nonce is generated at every 12 hours and a nonces are valid for 24 hours, so at any given point there are two nonces valid for a given$action
.The nonce is (a substring of) a hash of
$action
– the action$uid
– the user ID$i
– incrementor.The increments increases by 1 every 12 hours, so if the current nonce for a given user and action is a substring of
Then the previous nonce (for same user and action) is a substring of
Since both are valid nonces, when you check your received
$nonce
you check both for a match.I would like to bring a more in depth explanation on how do WordPress NONCEs really work. Also, the accepted answer needs to be updated with more accurate information, since now WordPress also takes into consideration the session token when creating the nonce. The link is a long article I wrote with more in depth explanations.
First, the accepted answer doesn’t say how the
if
works.so let’s dissect it shall we? Note that I’m using the current version of the statement since now it also takes into account the user’s session token
the PHP function
substr()
will return a part of the string.Here, the evaluation of
wp_hash()
is the string thatsubstr()
will evaluate and return part of. the-12
argument says to count 12 characters from the end of the string ( the result ofwp_hash()
‘s evaluation ),substr
starts from the end because of the minus sign telling it to do so.Finally, the last argument
10
, says to return 10 characters as the sub string.So count 12 characters from the end, then return 10, so the last 2 characters are stripped out of the returned result.
If you check a WordPress NONCE, it contains exactly 10 characters.
Another point I would like to clarify is the lifespan of a the WP NONCE.
We should not say that a nonce is valid for 24 hours, since in reality it’s alway less. A NONCE is valid for maximum 24 hours, minimum 12 hours. Better said, it is valid for 2 ticks.
A tick, as @Stephen Harris explained, is an increment of the current lifespan of the NONCE.
And by default a tick represents 12 hours calculated from UTC time. So at midnight +1 second and midday +1 second, the tick’s value is incremented by
1
unit.Given that the
$token
remains the same (the user did not logout and back in), thenwp_hash()
will always return the same hash for a given action and user id.This will remain true until the current tick value is incremented by 1. In which case, the hash will be different for the same action, same user id and same token.
This is how WordPress considers valid a nonce created 0-12 hours ago.
WordPress also consider valid a nonce created 12-24 hours ago by comparing the nonce with the hash function using the previous tick value. But this is misleading as I already mentioned. The 12-24 hours ago period doesn’t refer to the actual explicit creation time of a given NONCE ( in other words it’s not using the timestamp of the nonce creation ) but rather says in the period of hours ranging from 12-24 since it’s creation. Which actually refers to a unique tick value ( 1 less then the current one )
It would be less confusing if we considered nonce validity in terms of ticks, and not in terms of hours.
So the best way to describe the validity would be
I hope I made things clearer