PHP NONCE Library

By michael  |  August 12th, 2009  |  Published in Blogging, Uncategorized  |  23 Comments

[Download our nonce library]

We’ve recently developed our own PHP NONCE library for use with custom programming. Our version is loosely based on the implementation found inside the core WordPress software.

What is an NONCE?

Literally, the term refers to a number used once. In software development, it is often used as a security measure to ensure that certain links or forms are only available once, thereby preventing malicious attacks against the system. Read More

Where would I use an NONCE

An NONCE offers an additional level of security where sensitive actions may take place within your application. Take the following line of code as an example:

<a href="delete_post.php?post=003">Delete Post</a>

This link was poorly thought out if the application has no other security measures in place. Anyone could begin deleting posts by simply pointing their browser at the above link and changing the post number.

Well designed applications would only make that link available if the user was logged into the system with appropriate permissions. Furthermore, the delete_post.php script would ideally check to see if the user was logged into the system and if the user had appropriate permissions to delete that post. Is this enough security though?

Here are just two scenarios that could circumvent the above security measures:

  1. Depending on how the application’s user authentication works, it is certainly possible for a malicious user to spoof an authenticated user or to otherwise crack the authentication.
  2. Additionally, if you are a legitimate admin of the above mentioned application it would be possible for me to trick you by sending you a link or to this script. Once you clicked it, the post would be deleted.

How an NONCE prevents the above attacks

An NONCE is successful as an additional layer of security because it prevents actions initiated by links or REQUESTS from being used more than once. Every time a link or a form is printed on the screen, your NONCE functionality embeds a key / value pair to be sent to the receiving script. Every time that script is called, it checks for the key / value pair and then authenticates it on a pass / fail basis. If it passes, the action is performed, if it fails, the action is not performed.

The above link with an NONCE applied to it would resemble something like the following:

<a href="delete_post.php?post=003&_nonce=9c5fbfabb1">Delete Post</a>

The receiving script would then do the following:

  1. Check to see if the user is logged in with appropriate permissions (standard security)
  2. Check to make sure the NONCE key / value is set
  3. Authenticate the received NONCE using a library of functions.

How does an NONCE create and authenticate its key / value pairs?

While there are no hard and fast rules for creating an NONCE, most libraries will include the following components when generating an NONCE:

  • A secret key or ‘salt’ stored only on the server
  • A user ID (optional – makes the NONCE only work with a specific user)
  • An action name: ie: ‘delete-post’
  • A timestamp (allowing the NONCE to expire if never used)
  • A database of used NONCEs (optional and not used in our library)

Using all of the above components, an NONCE may be generated like this: secret-salt + user ID + action-name + timestamp. All of this is thrown into a hash that the receiving script can unpack and authenticate (Most of the time, the timestamp will be modified before being included and hashed).

The receiving script doesn’t actually ‘unpack’ the received key, rather it recreates it and compares. For instance, the delete_post script would combine the secret salt (which it knows) , the current users’s ID , use the delete-post action name, and a timestamp (modified appropriately). If any one of those components are off, the NONCE value generated by the receiving script will not match the one sent by the initial script and the NONCE will fail to authenticate.

How to use our NONCE library

  1. Download the zip file and unpack
  2. Include ft-nonce.php inside all your applications pages
  3. Embed one of our two generating functions in your links or forms
  4. Call the validating function at the top of your receiving scripts and do as you wish based on the validity of the NONCE.

If you need further example, you can check out the example here. The PHP file source file is included in the zip .

Feedback

We’ve only deployed this once and value your feedback. We will be more than happy to modify, enhance, and correct bugs as reported.

Responses

  1. Haitham says:

    September 13th, 2009 at 12:54 pm (#)

    Dear developers,

    Thank you very much for uploading your PHP NONCE Library for us. It is such a pleasure to find people working on this….

    I’ve downloaded your Library and started to play around with it! Would you please be so kind and answer my following questions?

    Questions

    1- In the ft_nonce_lib.php file, you define a global variable as, FT_NONCE_UNIQUE_KEY, yet you have not used anywhere at all, could I ask why, please?

    2- In this line: return md5( $i . $action . $user . $action ); did you mean to replace one of the $action variables with FT_NONCE_UNIQUE_KEY ? Was this your attention?

    3- In regards to your second goal stated as; “Additionally, if you are a legitimate admin of the above mentioned application it would be possible for me to trick you by sending you a link or to this script. Once you clicked it, the post would be deleted”.

    If the nonce value,9c5fbfabb1, is valid for, say, 5 minutes, then if an attacker was to send a link such as

    “’Delete Post’’

    ”, then the post will be deleted as soon as the admin clicks on the link, am I correct here or am I missing the point? Let’s suppose that the nonce value can be seen when the cursor is moved over the link. The link will not be seen of course if there was standard security in place. For example, if there is security checkers that only display the page if the user is logged on and has sufficient authorization to carry out the task. However, if there is a malicious screen scraper, then the link will be seen! Therefore, since the deleting process needs only to be carried out once, the nonce value used could perhaps be stored in a database to make sure that it can never be used again. Would you agree with this solution? Bye the way, would it help if the FT_NONCE_DURATION changed from 5 minutes to much lower period?

    Best regards,

    Haitham

  2. Haitham says:

    September 13th, 2009 at 5:18 pm (#)

    The link referred to in question 3 is as follows:
    a href=”delete_post.php?post=003&_nonce=9c5fbfabb1″ Delete Post a ;
    Thanks in advance,
    Regards,
    Haitham

  3. Peter Carter says:

    September 14th, 2009 at 12:25 pm (#)

    I am puzzled by your code. First, although the script will die if there is no FT_NONCE_UNIQUE_KEY, it doesn’t actually use the key to generate the nonce.

    Second, the treatment of the nonce timestamp is surely wrong.

    [code] $time = ceil(time()/FT_NONCE_DURATION)[/code]

    will return the time block in which the nonce is generated or tested. The ft_nonce_is_valid function should then test $nonce against, first, a nonce constructed using the previous time block ($time – 1) and, if that fails, against a nonce reconstructed using the present time block.

  4. Martin Lormes says:

    December 8th, 2009 at 12:24 pm (#)

    Your code has a major design flaw:

    The nonce is good for one certain time window, i.e. the nearer the user gets to the end of that windows the less time he or she has to submit the form, possibly less than one second.

    In the verfication function you would have to check whether the nonce is from the current or the previous time window!

    To see the effect go to your example and click the button every 10 seconds. This, by definition, should always be considered legitimate activity. However you will find that once every 2:30 or 5 minutes the activity is considered forbidden.

  5. glenn says:

    December 8th, 2009 at 1:10 pm (#)

    Hi Martin,
    Thanks for the feedback. If I understand you correctly though, you’re describing a feature, not a flaw. Most nonce libraries include an expiration feature. Ours is set to 5 min by default. If your implementation requires a longer time period you can modify that setting at the top of the script.

  6. Martin Lormes says:

    December 8th, 2009 at 2:34 pm (#)

    Hi Glenn,

    no, I’m not describing a feature. You must have misunderstood me.

    Let me try to give you an example:

    Let’s create a nonce key. Let’s assume time() returns 3000. Then line 58 of ft-nonce-lib.php calculates $i = 20. This “timestamp” is now hashed along with the $action and $user params given. This nonce is now valid until $i reaches 21, when it suddenly becomes invalid.

    Now time() turns to 3001 and suddenly, in line 58, $i is calculated to be 21, thus one second later the condition in line 50 becomes invalid.

    You actually need to check whether $nonce is equal to any of the hashes for $i or $i-1… plus your math is wrong. The divisor 2 in line 58 cuts your 5 minute timeframe in half.

    I’m in the process of writing a nonce class myself. It will be under the GPL, too, will be posted on my site, and I will send you a permalink or link and trackback this post if you like…

  7. John says:

    December 15th, 2009 at 10:32 am (#)

    I have a couple of implementation questions regarding ft-nonce-lib.php:

    You seem to be concatenating parameter inputs at some points when I believe you want to pass two separate paramters ($action and $user). This occurs on the call to ft_nonce_create() at ft_nonce_create_form_input(), and on the calls to ft_nonce_generate_hash() in ft_nonce_create() and in ft_nonce_is_valid(). While the code works as written, I don’t believe it is what you intended. Or am I missing something obvious?

    Also, where (and when aside from ft-nonce-lib startup) is NONCE_UNIQUE_KEY ever used? It appears that its sole purpose is to abort the script if it is undefined. The doc indicates that NONCE_UNIQUE_KEY is a ‘salt’ for the hash and I would expect to find it in an ‘md5()’ call, which I don’t. Did you mean for the final parameter ($action) in md5() in ft_nonce_generate_hash() to be NONCE_UNIQUE_KEY?

    This is exactly what I was thinking of implementing for a new website this morning and certainly looks quite good!

  8. Steve High says:

    March 21st, 2010 at 12:16 pm (#)

    This is a nice compact library. I was wondering if you considered tying the nonce to the user session instead of a time limit. If you did, what made you decide to go this route? I ask because I implemented the same thing, only with session data. I find that using session nonces can be problematic, especially if you are issuing a new nonce for each request.
    I might rewrite my nonce code to reflect what you’ve done, since the security benefits seem to me to be same.

  9. Tim Santeford says:

    March 30th, 2010 at 9:50 pm (#)

    No, Martin is correct. The line that looks like $i = ceil( time() / ( 300 ) ); has the problem. $i becomes the number of 5 minute intervals from the epoch. If the code is run a second before the end of an interval you will get one number and a second later you will get the next interval number. This current algorithm appears to work correctly when the initial nonce is created near the beginning of one of these intervals. It will tend to fail when the nonce is created towards the end of an interval. If I find a solution for this I will post it.

  10. Sam says:

    April 28th, 2010 at 2:51 pm (#)

    Uh? The purpose on a “number used once” is… to be used only once. If it’s possible to reuse the same nonce, this implementation is not ok.

  11. Jim Hyslop says:

    July 18th, 2010 at 8:39 am (#)

    I see what Martin is getting at.

    In the function ft_nonce_generate_hash, the statement

    $i = ceil( time() / ( FT_NONCE_DURATION / 2 ) );

    breaks down the current time into 2:30 windows (not 5 minutes because you divide FT_NONCE_DURATION by 2). The nonce must be validated before the end of the current window. To see this more clearly, add this statement into ft_nonce_generate_hash:

    echo "Nonce generated at " . strftime("%H:%M:%S ", time() ) . $i . "
    ";

    and add this loop after the function:

    $endnonce = time()+FT_NONCE_DURATION;

    while ( $endnonce > time() )

    {

    ft_nonce_generate_hash();

    sleep( 1 );

    }

    then run the program from the command line for a couple of minutes. You will see output something like this:

    Nonce generated at 12:19:59 8529800

    Nonce generated at 12:20:00 8529800

    Nonce generated at 12:20:01 8529801

    Nonce generated at 12:20:02 8529801

    Nonce generated at 12:20:03 8529801

    Nonce generated at 12:20:04 8529801

    Nonce generated at 12:20:05 8529801

    Nonce generated at 12:20:06 8529801

    Nonce generated at 12:20:07 8529801

    Nonce generated at 12:20:08 8529801

    [...]

    Nonce generated at 12:22:23 8529801

    Nonce generated at 12:22:24 8529801

    Nonce generated at 12:22:25 8529801

    Nonce generated at 12:22:26 8529801

    Nonce generated at 12:22:27 8529801

    Nonce generated at 12:22:28 8529801

    Nonce generated at 12:22:29 8529801

    Nonce generated at 12:22:30 8529801

    Nonce generated at 12:22:31 8529802

    Nonce generated at 12:22:32 8529802

    Nonce generated at 12:22:33 8529802

    All nonces generated from 12:20:01 to 12:22:30 inclusive had the same time value, 8529801. The nonces generated starting at 12:22:31 had a different time value, 8259801. So, if I got a nonce generated at 12:20:01, I would have 2:30 to submit my request before the nonce expired. If I got a nonce generated t 12:22:30, I would have one second.

    To eliminate this problem, I'd modify it so that you pass the timestamp into the ft_nonce_generate_hash function, and add it as a hidden input field on the form. When you validate the nonce, you can then use the timestamp in the form. Since the timestamp is part of the MD5 hash, if someone tries to modify the nonce issue timestamp, the MD5 hash will be different and validation will fail. You can then compare the timestamp returned in the input field against the current timestamp to see if it's expired.

  12. Jim Hyslop says:

    July 18th, 2010 at 8:55 am (#)

    BTW, two things:

    first, I meant to write "The nonces generated starting at 12:22:31 had a different time value, 8259802" (wrong time value).

    Secondly, by passing in the timestamp, you can now differentiate between an expired nonce and an invalid nonce, so your error messages can be a little more friendly :-)

    Oh, by the way (OK, that's three BTWs, but who's counting? :-) you might want to include a link on the example page to get back to here more easily, rather than hitting the "back" button.

  13. Anon Guy says:

    August 12th, 2010 at 5:04 pm (#)

    I think I can address Martin's concern:

    Your code is:

    function ft_nonce_generate_hash( $action='' , $user='' ){

    $i = ceil( time() / ( FT_NONCE_DURATION / 2 ) );

    return md5( $i . $action . $user . $action );

    }

    Roughly

    $i = ceil( time / 150) where duration is 300

    at time 3000, $i = ceil(20) = 20

    at time 3001, $i = ceil(20.006667) = 21

    So a nonce could be 'invalid' one second after it's creation…. Hence the concept of windows he was discussing.

    I think the only proper usage would be for you to add a time param as well. You pass the user time=3213123, and then the user passes this back. Check that 3213123 is within 5 minutes of now() and then check the nonce.

  14. Anon Guy says:

    August 12th, 2010 at 5:11 pm (#)

    alternatively,

    if one hash fails, you could just try to hash using a time from the prior window and see if that passes.

  15. Bob says:

    August 23rd, 2010 at 8:28 am (#)

    The idea is good but still needs a lot of work.

    The nonce can be easily predicted by using simple brute force javascript.

  16. Damokles says:

    August 30th, 2010 at 7:25 am (#)

    In the ft_nonce_generate_hash() function, shouldnt this:
    return md5( $i . $action . $user . $action );
    look like this:
    return md5( $i . $action . $user . FT_NONCE_UNIQUE_KEY );
    As without the secret key, this hashes could be easily reproduced (assumed you know the action name and optionally, if existent, your user id)

  17. flieingzbu says:

    November 15th, 2010 at 5:08 pm (#)

    A simple fix for the nonce time issue could potentially be checking for a percentage value up to 50% and allow the preceeding or current nonce to work and if after 50% use the next and current nonce as acceptible candidates. This would help in cases where you take the time window and divide it by half to increase precision.

    This would probably have a different goal than the original intent however.

  18. Jason says:

    December 17th, 2011 at 5:00 am (#)

    Could someone modify this to make it work with the passing of timestamp like mentioned in the comments?

  19. Agree with Damokles says:

    June 15th, 2012 at 9:39 am (#)

    I know I’m coming here late, but I agree with Damokles — you’re not using the salt anywhere!

  20. Agree with Damokles says:

    June 15th, 2012 at 9:42 am (#)

    As a followup, if anyone stumbles here, check out how WordPress handles nonces:
    wp_verify_nonce in http://core.trac.wordpress.org/browser/tags/3.4/wp-includes/pluggable.php#L0

    They take care of the “window” issue as well.

  21. akhasis says:

    August 1st, 2012 at 9:51 am (#)

    Damokles is right, this library is highly insecure! The unique key is not used at all in the code!

    Apart from this, there are some other flaws. For example, tho the function ft_nonce_generate_hash( $action=” , $user=”) is meant to have (a max of) 2 parameters, it is always called with only one (a concatenation of $action and $user):

    ft_nonce_generate_hash( $action . $user );

    Although it could be done on purpose, then there is no point in declaring ft_nonce_generate_hash with two (although optional) parameters.

  22. Strongly Linking Pages Together via URL Nonces. | ChrisBrittain.com says:

    September 2nd, 2012 at 8:17 pm (#)

    [...] this code is basically taken in full and modified from here. The commentors on that page noted a design flaw whereby the validation checker can’t tell [...]

  23. How does this PHP nonce library work? - PHP Solutions - Developers Q & A says:

    August 16th, 2013 at 9:47 pm (#)

    […] http://fullthrottledevelopment.com/php-nonce-library#download, there is a PHP nonce library, but there are a few things that I don’t know understand. The […]