PHP NONCE Library
By michael | August 12th, 2009 | Published in Blogging, Uncategorized | 13 Comments
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:
- 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.
- 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:
- Check to see if the user is logged in with appropriate permissions (standard security)
- Check to make sure the NONCE key / value is set
- 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
- Download the zip file and unpack
- Include ft-nonce.php inside all your applications pages
- Embed one of our two generating functions in your links or forms
- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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)
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.
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 [...]