1

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-includes\pluggable.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,10th part of 8ae70558afac4a6951e3bc0f9ef1f59a supposed to be a check for "0-12 hours ago"?

2 Answers 2

3

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

wp_hash($i . $action . $uid, 'nonce')

Then the previous nonce (for same user and action) is a substring of

wp_hash(($i - 1) . $action . $uid, 'nonce')

Since both are valid nonces, when you check your received $nonce you check both for a match.

1

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

$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );

the PHP function substr() will return a part of the string.

Here, the evaluation of wp_hash() is the string that substr() will evaluate and return part of. the -12 argument says to count 12 characters from the end of the string ( the result of wp_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), then wp_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

  • 0-12 hours == current tick value == t
  • 12-24 hours == current tick value - 1 == previous tick value == t - 1

I hope I made things clearer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.