Nonces That Are Used Only Once

A while back in this article on wptuts I talked about nonces, and how they work. Normally, nonces are ‘one use only’ – once they are used they are stored, and never accepted again. WordPress, on the other hand, generates a new nonce every 12 hours, each with a life span of 24 hours. They can be used as many times during those 24 hours as needed. And that is, usually, all that is necessary. However instances may occur when you want to create and check nonces that are actually only used once.

Telling the time()

So how can we achieve this? First we want to create a fresh nonce for (essentially) every page load – since if our nonce can only be used once, then you’ll want to be able to do more than one action every 12 hours. I could force WordPress to generate new nonces by shortening the life of the nonce – but this is not really what I want and I might end up with a nonce, valid for one second. Not great.

Instead the idea is to create a standard nonce, whose ‘action’ includes a timestamp. This ensures a fresh nonce is generated every second (say) – which is enough for our purpose (nonces are unique to the user, and should also be unique to the action being taken, and if applicable, the object the action is being applied to). Of course to validate the nonce, we’ll also needed to send the timestamp.

So to create our nonce, we just use wp_create_nonce, but after concatenating it with a timestamp. The returned ‘nonce’ now consists of two parts: the nonce value and timestamp, which we join with hyphen

function sh_create_onetime_nonce($action = -1) {
    $time = time();
    $nonce = wp_create_nonce($time.$action);
    return $nonce . '-' . $time;
}

We can then embed the nonce and timestamp together in the form/url – or however the action is being triggered.

 <form>
    <input type="hidden" name="nonce" value="<?php echo sh_create_onetime_nonce( 'this-action' );?>"/>

    <input type="submit" value="Perform this action">
 </form>

Validating the nonce

Now to verify our nonce. The nonce consists of the original nonce generated by WordPress, combiend witht he timestamp of when it was generated (separated by a hyphen). So first we need to extract the constituent parts.

Concatenating the timestamp part with the action, we use the wp_verify_nonce function agains the nonce part. This checks that the nonce is valid. Of course, it already has a validity lifespan of 24 hours – but in order to reduce the number of used nonces we might have to store, I’ve opted to impose a 1 hour life span as well.

If the nonce is valid, we then check that it hasn’t be used. Used nonces are stored in the database as a serialised array. The array is of the form array( $nonce => $expires_timestamp ) and ordered by timestamp. (I did consider storing them in a separate table, but this is beyond the scope of the article and if we try to keep the number of nonces that might be stored down – say by setting the 1 hour life span, storing them in array shouldn’t present any problems).

If the nonce value hasn’t be used, we proceed to remove any ‘expired’ nonces (ones that would now fail the 1 hour life check) and add this used nonce into the array – and then sorting.

function sh_verify_onetime_nonce( $_nonce, $action = -1) {

    //Extract timestamp and nonce part of $_nonce
    $parts = explode( '-', $_nonce );
    $nonce = $parts[0]; // Original nonce generated by WordPress.
    $generated = $parts[1]; //Time when generated

    $nonce_life = 60*60; //We want these nonces to have a short lifespan
    $expires = (int) $generated + $nonce_life;
    $time = time(); //Current time

    //Verify the nonce part and check that it has not expired
    if( ! wp_verify_nonce( $nonce, $generated.$action ) || $time > $expires )
        return false;

    //Get used nonces
    $used_nonces = get_option('_sh_used_nonces');

    //Nonce already used.
    if( isset( $used_nonces[$nonce] ) ) )
        return false;

    foreach ($used_nonces as $nonce=> $timestamp){
        if( $timestamp > $time )
            break;

        //This nonce has expired, so we don't need to keep it any longer
        unset( $used_nonces[$nonce] );
    }

    //Add nonce to used nonces and sort
    $used_nonces[$nonce] = $expire;
    asort( $used_nonces )
    update_option( '_sh_used_nonces',$used_nonces );
}

And there we have it, nonces that can actually only be used once.

5 thoughts on “Nonces That Are Used Only Once

  1. Nonces that are used only once : Post Status

  2. You could also use a transient with a lifetime of 1 hour for storing the used nonce. WordPress will than automatically delete the used nonce and you do not have to care about the storing and deleting.

    • Hi Ralf,
      You could, but because of the way WordPress implements nonces a new nonce will only be generated every half hour. For example, WordPress generates a nonce when I edit a post. When I update that post, I’m returned to the post edit screen and the same nonce is used again. Reducing the lifespan will ensure the nonce is ‘refreshed’ more frequently (though still not only being used once!) – but this presents its own problems. E.g. If I reduce it to 5 minutes, I better not take longer than 5 minutes to perform any action :).

    • I understand you now :). That’s a really good idea, though I would just remove the nonce rather than set it to ‘used’. Also, using just the time for the key will cause a conflict in the unlikely event that two users try to do something at the exact same time!

      On a side note I opted to store the nonces in an array, rather than in their own row to prevent unused nonces clogging up the database (and there’s not transient clean-up) – though I’m not really sure of what practical impact that would actually have.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>