Is this a secure way to set the token CSRF? - php

I am wondering if this is a secure way to set a token, unless there actually is a token generated, I generate one, and use it throughout the applications and those forms. One token per session?
if (!isset($_SESSION['token'])) {
$data['token'] = uniqid(rand(), true);
session_regenerate_id();
$_SESSION['token'] = $data['token'];
}
Would it be necessary to clear out the token on a submitted form? or just stay with it, even though I submitted a form?

If you don't know these links, this should help you understand some scenarios and specifically this will tell you the DOs and DONT's. Hope it helps.

Personally I would generate a new token for every form I want to display. If you do it this way, someone just needs a session cookie to read your token and use it as long as the session stays active.
In my applications I generate a token for each form display like this:
<?php
$token = uniqid(rand(), true);
$_SESSION['csrf_tokens'][$token] = true;
HTML
<form>
<input type="hidden" name="token" value="<?php echo $token ?>" />
</form>
On form validation I check for that token like this:
if (isset($_SESSION['csrf_tokens'][$token]) && $_SESSION['csrf_tokens'][$token] === true) {
unset($_SESSION['csrf_tokens'][$token]);
// additional code here
}

I am wondering if this is a secure way to set a token
It depends on how secure your web app needs to be. This line is not cryptographically secure (As warned in PHP docs for uniqid() and rand()):
uniqid(rand(), true);
It may be feasible for an attacker to determine/brute force this if the time of token generation is known/determined and the rand() seed is known/determined. However, for your purposes it may be fine as it will still prevent CSRF attacks where the attacker has no knowledge of the token value.
One token per session?
Using one token per session may be fine for your purposes. However, be aware:
If a session is n minutes long then an attacker has an n minute window to attempt to determine or obtain your token value and execute a CSRF attack. Whereas this risk is reduced when tokens are generated per form or when the token is regenerated periodically as they are not long lived enough.
Using a single token per session exposes all of your application's functionality (that uses that token) to attack should an attacker determine/obtain the token. Whereas using a token per form restricts an attack to a single form.
Would it be necessarery to clear out the token on a submitted form? or just stay with it, even though i submitted a form?
It depends upon how high value a target your application is for attackers and the level of disruption an attack would cause you. Your existing measure makes it difficult to execute CSRF attacks but if it is high value and you have very determined attackers then you may want to reduce the risk of CSRF more by:
Using cryptographically secure tokens to prevent risk of determining or brute forcing the token value.
Regenerating the token periodically to reduce token lifespan, decreasing the attack window if the token is determined or obtained.
Generating tokens per form to restrict attacks to a single form in the event that the token be determined or obtained.

Rather than use per-session token i would prefer per-form/url token for additional security some might argue that per-request token is most secured but affects usability.
I also think its better to separate your session storage from your token storage and use something like Memcache. This is better when you need speed using multiple application servers etc. I also prefer it because i can add a custom expiration to the token without having to affect the whole session
Here is a typical example
HTML
<form method="POST" action="#">
IP:<input type="text" name="IP" /> <input type="hidden" name="token"
value="<?php echo Token::_instance()->generate(); ?>" /> <input
type="Submit" value="Login" />
</form>
Processing
$id = "id44499900";
Token::_instance()->initialise($id); // initialise with session ID , user ID or IP
try {
Token::_instance()->authenticate();
// Process your form
} catch ( TokenException $e ) {
http_response_code(401); // send HTTP Error 401 Unauthorized
die(sprintf("<h1>%s</h1><i>Thief Thief Thief</i>", $e->getMessage()));
}
Class Used
class Token {
private $db;
private $id;
private static $_instance;
function __construct() {
$this->db = new Memcache();
$this->db->connect("localhost");
}
public static function _instance() {
self::$_instance === null and self::$_instance = new Token();
return self::$_instance;
}
public function initialise($id) {
$this->id = $id;
}
public function authenticate(array $source = null, $key = "token") {
$source = $source !== null ? $source : $_POST;
if (empty($this->id)) {
throw new TokenException("Token not Initialised");
}
if (! empty($source)) {
if (! isset($source[$key]))
throw new TokenException("Missing Token");
if (! $this->get($this->id . $source[$key])) {
throw new TokenException("Invalid Token");
}
}
}
public function get($key) {
return $this->db->get($key);
}
public function remove($key) {
return $this->db->delete($key);
}
public function generate($time = 120) {
$key = hash("sha512", mt_rand(0, mt_getrandmax()));
$this->db->set($this->id . $key, 1, 0, $time);
return $key;
}
}
class TokenException extends InvalidArgumentException {
}
Note : Note that the example might affect "Back" button or refresh because the token would be automatically deleted after 120 sec and this might affect user friendly capability

Can you please refer following site, this may get some ideas.
1.) https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
2.) http://blog.whitehatsec.com/tag/session-token/
Thanks for reply.

I've already answered a similar question on a different forum: here. Hopefully that's helpful. It explains the basic process of the CSRF prevention and links to some code for a CSRF framework.
If you want higher security, change the token after each request for each session. If you want better usability, keep one token per session.

Related

How do I ensure privilege to update record?

I received some pen tests results.
The results say that anybody can update any record by just changing a certain id.
How could I ensure that the user can only update his own record in this function?
public function actionUpdateProfile()
{
$postdata = file_get_contents("php://input");
$response = array("status" => "1", "message" => "Profile update successful.");
$data = json_decode($postdata);
$model = HosDoctors::model()->findByPk($data->doctor_id);
foreach ($data->fields1 as $field) {
$_POST[$field->name] = $field->value;
}
$enc = NEW bCrypt();
$model->attributes = $_POST;
if ($model->save()) {
$response = array("status" => "1", "message" => "Profil erfolgreich aktualisiert");
} else {
pr($model->getErrors());
}
echo json_encode($response);
die;
}
Would it be sufficient to simply check for
if (cookie == $data->doctor_id)
{
//ok
}
else
{
//we are not logged as the user id that we want to update, so deny updating
die;
}
I am assuming the person has "logged in" in some way so that you know "who they are". While security is complex and is definitely not a one-answer topic; at its simplest level, once you have identified the user, use PHP session handling to persist their identity across one or many http/s requests, then internally access any related information using the session cookie id for the duration of the session.
There are several potential issues in this function (unless you posted a heavily edited version). I'll note them as comments.
public function actionUpdateProfile()
{
$postdata = file_get_contents("php://input");
$response = array("status" => "1", "message" => "Profile update successful.");
// Never initialize responses until you really *must*. Chances that a partially prepared response might be output are slight, but why run risks?
// And actually you **do** reinitialize $response later on!
$data = json_decode($postdata);
// You are not verifying that $data *exists* (i.e. the JSON data was, indeed, JSON). You should check that $data is not NULL and that it does have **all** the required fields and that they are valid.
// This is the point where you validate $data->doctor_id, by the way. Or you check that patient_data matches with whatever you have in your $_SESSION or Session app object.
$model = HosDoctors::model()->findByPk($data->doctor_id);
// This is a bad practice. Yes, you have some code that relies on
// _POST. If necessary, wrap it in another code that will set up
// _POST from an input and then delete it. Otherwise you're leaking
// data into a superglobal. You don't want to do that.
foreach ($data->fields1 as $field) {
$_POST[$field->name] = $field->value;
}
// Why are you initialising $enc?
$enc = NEW bCrypt();
// This is not very good. $_POST could contain *other* information
// unless it's been sanitized outside the function.
// I would prepare a setter function, $model->setArray($data), that
// would verify the validity of the attributes before setting them.
$model->attributes = $_POST;
if ($model->save()) {
$response = array("status" => "1", "message" => "Profil erfolgreich aktualisiert");
} else {
pr($model->getErrors());
}
// This works in 90% of the browsers and scenarios. But I'd
// set up a function that would also send the appropriate
// Content-Type headers to satisfy the remaining 10%.
echo json_encode($response);
die;
// e.g. Utilities::jsonResponse($response);
}
Would it be sufficient to simply check for
if (cookie == $data->doctor_id) {
//ok } else {
//we are not logged as the user id that we want to update, so deny updating
die; }
try checking by user token instead of user-id because user-id can be found within some page url or body but the token is hard to get unless using network sniffing technics.
you can generate user token by any token generation method and then store it as a column in the user table in your database.
token generation method e.g
$token = bin2hex(openssl_random_pseudo_bytes(16));
# or in php7
$token = bin2hex(random_bytes(16));
Would it be sufficient to simply check for
if (cookie == $data->doctor_id)
{
//ok
}
else
{
//we are not logged as the user id that we want to update, so deny updating
die;
}
No, in principle this is actually the same as you already have: the ID is still part of the request and can be changed like you got it reported.
Instead ensure the request is first of all authorized, then fetch the related data from the identity and access management system.
If you do not have anything to manage identity and access, choose a system and integrate it first.
From the code you've posted so far it is not visible (to me) whether or not such a system exists. Therefore I can not specifically answer on that detail.
Authentication/Authorization of requests is part of the HTTP protocol and is supported at a basic level in PHP out of the box. See:
HTTP authentication with PHP Docs
If you want to roll your own completely you can consider HTTP state management with PHP sessions, but as you ask the question the way you ask it (with all due respect), my suggestion is to take something that already exists, is proven and easy for you to understand and integrate (less complexity = better software and less flaws/holes).
Take the event of the pen test and its result to address the design issues first and find a high level solution not that you need to patch multiple endpoints individually only to address the same basic flaw.
Let the system work for you.
After the pen-test I would also suggest the code is reviewed with dedication towards the found issues and the code writers are trained based on the results of both.
Would it be sufficient to simply check for ...
Yes, given the cookie is from a safe source and all its properties are secure. E.g. only the identity and access management system can add it to the HTTP request that hits the PHP SAPI.
I would rate this event very unlikely as you needed to ask the question.
And it would be very unorthodox if it would contain a concrete id. There should always be a HTTP state management and it should be rolling to protect against replay attacks with credentials or at least limit the time-frame in which they are valid.
But purely technically, with the right design and implementation I can imagine there can be systems that work this way (HTTP request with cookie as secure token).
Take care.

How to access to an external database securely?

I'm developing a mobile app which has to access to an external webapp (PHP + Codeigniter) to administrate the actions queried by ajax.
So by this way, there is a problem. If anyone see the urls used, could delete rows, or modify the user's info from the database. So I thought in this system to aboid this:
After a sucessful login I would do this:
// getToken : https://stackoverflow.com/a/13733588/2154101
$this->session->set_userdata('private_token', getToken(50));
$public_token = getToken(50);
$this->session->set_userdata('secure_token', md5("$private_token:$public_token"));
$data['token'] = $public_token;
// some stuff ...
// send $data in JSON
Then the client would the public token in the next query I would do this on the server:
$public_token = $this->input->post('token');
$data['token'] = get_public_token($public_token);
// some stuff ...
// send $data in JSON
Where get_public_token is within a helper with this code:
public get_public_token($public_token) {
$last_secure_token = $this->session->userdata('secure_token');
$private_token = $this->session->userdata('private_token');
$actual_token = md5("$private_token:$public_token");
if ($actual_token === $last_secure_token) {
$public_token = getToken(50);
$this->session->set_data('private_token', getToken(50));
$this->session->set_data('secure_token', md5("$private_token:$public_token"));
return $public_token;
} else { // you are cheating me ...
$this->session->sess_destroy();
redirect('/');
}
}
So only the user of this session could modify the data of the database.
I'm just trying to do the same explained here: https://stackoverflow.com/a/17371101/2154101
The session are encrypted, and I store them in a database too.
Do you think this method will work ok? Am I missing something important?
You should create an API for your mobile application. Create a authentication mechanism.
If your database holds user specific data, then you should create account for each user. So if the user sniffs the network and tries to call the api manually, then he could only change he's own data.
There are some API libraries for php out there, you should look into that.
Actually your solution is doing more than necessary. The only token of interest is the public_token sent back and forth. So you can throw away private_token and secure_token from session data, keeping only public_token for checking. Your current check is something like (X + 5)/2 == (14 + 5)/2 (is [received_token + 5]/2 equal to [14 + 5]/2 ?) when you can simplify to X == 14.
However if someone is sniffing the network, he can get the last token sent to a client and use it to hijack into that session. He can execute anything while the original client doesn't send a request with the outdated token, killing the session.
A better solution would be creating a secure_key after login and keep it at both ends (client and server). Then server would keep sending a new public_token at each response, but the client would send a md5(secure_key + public_token) at requests. This would narrow even more the hijacking window to the exact point where the session started. Without the original key, attackers can't create a valid md5.
However we are talking about minor hacking fans here. Anyone more zealous could hack that anyway. If you are concerned about that, then throw away all that stuff and simply use a HTTPS connection. With a trusted connection your sessions and access control rules are protected.
The better way is create API using SOAP or SAML2.
OAuth can be a very good solution: http://oauth.net/. It takes care of token and has a very secured API! If you wish to support secure authentication of web application + mobile application then it can be a good/proven solution!
On the other hand, it really depends on how complex your current system is and how the system is going to be in future.

CSRF token not validated from times to times

I use Codeigniter/PHP. I use CSRF tokens (not the CI native version as I have my own form implementation) and from time to time the token is not validated.
The CSRF token is created once per session:
function create_csrf_token() //If needed, creates a session variable; returns a hash to use for CSRF protection
{
$CI =& get_instance();
if($CI->session->userdata('csrfToken')) //the token already exists: use its hash
{
$csrfHash = $CI->session->userdata('csrfToken');
}
else //no token yet: create session variable + set its hash
{
$csrfHash = base64_encode(hash('sha256', uniqid(serialize($_SERVER), true), true));
$CI->session->set_userdata(array('csrfToken' => $csrfHash));
}
return $csrfHash;
}
It is passed to the form without issues in the csrfToken hidden input field, with htmlspecialchars applied to it (using urlencode makes no difference):
echo '<input type="hidden" name="'.$this->name.'" value="'.htmlspecialchars($this->value).'">';
This field has a validation rule verify_csrf:
public function verify_csrf($token)
{
$CI =& get_instance();
if($CI->session->userdata('csrfToken') && $CI->session->userdata('csrfToken') == $token) return true;
else
{
$this->set_message('verify_csrf', 'Invalid token');
return false;
}
}
This is where things get weird. Sometimes $token is not correct and looks like corrupted data. Here are a couple of examples:
Error:
Value in $CI->session->userdata('csrfToken'): 6cT3O0KTOk7cVlear71lU7KKFlGONt4rS2HjNoSVFRM= (correct)
Value in $token: 6cT O0KTOk7cVlear71lU7KKFlG (4th character changed and missing end of string)
No error:
Value in $CI->session->userdata('csrfToken'): AiAgGqqxTxuCxN7h5HHRtcJjmHJVMRksBYbq6Dx4Kv4=
Value in $token: AiAgGqqxTxuCxN7h5HHRtcJjmHJVMRksBYbq6Dx4Kv4=
Any idea? I have checked and rechecked, the CRSF token is correctly set everywhere except in $token in my validation callback. And it only happens for certain tokens...
EDIT: so it seems that the base64 encoding was causing the issue (why, I don't know). I have replaced
$csrfHash = base64_encode(hash('sha256', uniqid(serialize($_SERVER), true), true));
by
$csrfHash = random_string('sha1');
This is just a wild guess, but could it be the combination of the Base64 encoding and submitting the form via HTTP POST, like described here:
POST Base64 encoded data in PHP
The solution could then be to urlencode() the token before posting?
EDIT: The solution turns out to be dropping the base64 encoding of the token in favor of a plain sha256-hash as a token. See comments below.
Please use the config for the CSRF.
You can enable csrf protection by opening your application/config/config.php file and setting this:
$config['csrf_protection'] = TRUE;
// This will automatically generate the hidden field for form_open(params) function, so you no need to do it every time. This feature is included the frame work.
To avoid CSRF to specific URIs
http://ellislab.com/forums/viewthread/182631/

PHP CSRF Token code. Sufficient security?

Having recently protected my site the best I can against XSS I am now in the process of protecting against CSRF, having watched and read some articles on the matter I have created the below code.
I would like to know whether my implementation is correct in using a string in the database to help security. Is the anything I should be doing differently?? Should I be checking the database on both sides???
Code:
if(!isset($_SESSION['register_token'])){
$keytype = 'register';
$getregisterkey = mysql_query("SELECT key FROM tokenkeys WHERE type='".$keytype."' ") or die(mysql_error());
while ($row = mysql_fetch_array($getregisterkey))
{
$registerkey = mysql_real_escape_string($row['key']);
};
$_SESSION['register_token']=sha1(uniqid(rand(), TRUE).$registerkey);
$_SESSION['register_token_time']=time();
}
<input type="hidden" name="token" value="<?php echo $_SESSION['register_token'];?>" />
if($_POST['register_token']==$_SESSION['register_token']){
$register_token_age=time()-$_SESSION['register_token_time'];
if($register_token_age=>300){
//process form
} else{
//valid token but expired
}
} else{
die('Access Forbidden')
}
XSRF tokens are only as safe as the channel over which the page is sent. Use https and only https on this form and only submit it to an https endpoint. Otherwise a MITM can get your XSRF token from the form as it is served, or as it is submitted.
while ($row = mysql_fetch_array($getregisterkey))
{
$registerkey = mysql_real_escape_string($row['key']);
};
will never execute when there are zero rows which case you don't get much entropy from $getregisterkey when you later do
$_SESSION['register_token']=sha1(uniqid(rand(), TRUE).$registerkey);
so I would make sure that your implementation fails-fast if there are zero rows returned. Maybe change to if ($row = mysql_fetch_array(...)) { ... } else { /* abort */ } since you get no benefit from extra rows.
The rand() needs to be either truly random or a cryptographically strong PRNG.
I am not familiar with PHP's standard libraries but [wikipedia] suggests rand() is not cryptographically strong. wikipedia says
There are proposals for adding strong random number generation to PHP.
Strong cryptography in PHP suggests using openssl_random_pseudo_bytes()
Don’t use rand() or mt_rand()
To generate a cryptographically strong random number in PHP you have to use the function openssl_random_pseudo_bytes() of the OpenSSL library.
If you use weak randomness then an attacker can observe the numbers you generate (by requesting multiple version of the form and parsing out the hidden input) and use that to figure out what the next numbers might be and forge CSRF tokens.
If an attacker modify the 'register_token_time' session property then they can avoid your XSRF checks.
For example, if you have a page that does
$_SESSION[$_POST['x']] = $_POST['y'];
then an attacker can POST
x=register_token&y=pwnd
to replace the register_token stored in the session and then send a post with
token=pwnd
and bypass your XSRF protection.

How to create and use nonces

I am running a website, and there is a scoring system that gives you points for the number of times you play a game.
It uses hashing to prove the integrity of http request for scoring so users cannot change anything, however as I feared might happen, someone figured out that they didn't need to change it, they just needed to get a high score, and duplicate the http request, headers and all.
Previously I'd been prohibited from protecting against this attack because it was considered unlikely. However, now that it has happened, I can. The http request originates from a flash game, and then is validated by php and php enters it into the database.
I'm pretty sure nonces will solve the issue, but I'm not exactly sure how to implement them. What is a common, and secure way of setting up a nonce system?
It's actually quite easy to do... There are some libraries out there to do it for you:
PHP Nonce Library
OpenID Nonce Library
Or if you want to write your own, it's pretty simple. Using the WikiPedia page as a jumping off point, In pseudo-code:
On the server side, you need two client callable functions
getNonce() {
$id = Identify Request //(either by username, session, or something)
$nonce = hash('sha512', makeRandomString());
storeNonce($id, $nonce);
return $nonce to client;
}
verifyNonce($data, $cnonce, $hash) {
$id = Identify Request
$nonce = getNonce($id); // Fetch the nonce from the last request
removeNonce($id, $nonce); //Remove the nonce from being used again!
$testHash = hash('sha512',$nonce . $cnonce . $data);
return $testHash == $hash;
}
And on the client side:
sendData($data) {
$nonce = getNonceFromServer();
$cnonce = hash('sha512', makeRandomString());
$hash = hash('sha512', $nonce . $cnonce . $data);
$args = array('data' => $data, 'cnonce' => $cnonce, 'hash' => $hash);
sendDataToClient($args);
}
The function makeRandomString really just needs to return a random number or string. The better the randomness, the better the security... Also note that since it's fed right into a hash function, the implementation details don't matter from request to request. The client's version and the server's version don't need to match. In fact, the only bit that needs to match 100% is the hash function used in hash('sha512', $nonce . $cnonce . $data);... Here's an example of a reasonably secure makeRandomString function...
function makeRandomString($bits = 256) {
$bytes = ceil($bits / 8);
$return = '';
for ($i = 0; $i < $bytes; $i++) {
$return .= chr(mt_rand(0, 255));
}
return $return;
}
Nonces are a can of worms.
No, really, one of the motivations for several CAESAR entries was to design an authenticated encryption scheme, preferably based on a stream cipher, that is resistant to nonce reuse. (Reusing a nonce with AES-CTR, for example, destroys the confidentiality of your message to the degree a first year programming student could decrypt it.)
There are three main schools of thought with nonces:
In symmetric-key cryptography: Use an increasing counter, while taking care to never reuse it. (This also means using a separate counter for the sender and receiver.) This requires stateful programming (i.e. storing the nonce somewhere so each request doesn't start at 1).
Stateful random nonces. Generating a random nonce and then remembering it to validate later. This is the strategy used to defeat CSRF attacks, which sounds closer to what is being asked for here.
Large stateless random nonces. Given a secure random number generator, you can almost guarantee to never repeat a nonce twice in your lifetime. This is the strategy used by NaCl for encryption.
So with that in mind, the main questions to ask are:
Which of the above schools of thought are most relevant to the problem you are trying to solve?
How are you generating the nonce?
How are you validating the nonce?
Generating a Nonce
The answer to question 2 for any random nonce is to use a CSPRNG. For PHP projects, this means one of:
random_bytes() for PHP 7+ projects
paragonie/random_compat, a PHP 5 polyfill for random_bytes()
ircmaxell/RandomLib, which is a swiss army knife of randomness utilities that most projects that deal with randomness (e.g. fir password resets) should consider using instead of rolling their own
These two are morally equivalent:
$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);
and
$_SESSION['nonce'] []= random_bytes(32);
Validating a Nonce
Stateful
Stateful nonces are easy and recommended:
$found = array_search($nonce, $_SESSION['nonces']);
if (!$found) {
throw new Exception("Nonce not found! Handle this or the app crashes");
}
// Yay, now delete it.
unset($_SESSION['nonce'][$found]);
Feel free to substitute the array_search() with a database or memcached lookup, etc.
Stateless (here be dragons)
This is a hard problem to solve: You need some way to prevent replay attacks, but your server has total amnesia after each HTTP request.
The only sane solution would be to authenticate an expiration date/time to minimize the usefulness of replay attacks. For example:
// Generating a message bearing a nonce
$nonce = random_bytes(32);
$expires = new DateTime('now')
->add(new DateInterval('PT01H'));
$message = json_encode([
'nonce' => base64_encode($nonce),
'expires' => $expires->format('Y-m-d\TH:i:s')
]);
$publishThis = base64_encode(
hash_hmac('sha256', $message, $authenticationKey, true) . $message
);
// Validating a message and retrieving the nonce
$decoded = base64_decode($input);
if ($decoded === false) {
throw new Exception("Encoding error");
}
$mac = mb_substr($decoded, 0, 32, '8bit'); // stored
$message = mb_substr($decoded, 32, null, '8bit');
$calc = hash_hmac('sha256', $message, $authenticationKey, true); // calcuated
if (!hash_equals($calc, $mac)) {
throw new Exception("Invalid MAC");
}
$message = json_decode($message);
$currTime = new DateTime('NOW');
$expireTime = new DateTime($message->expires);
if ($currTime > $expireTime) {
throw new Exception("Expired token");
}
$nonce = $message->nonce; // Valid (for one hour)
A careful observer will note that this is basically a non-standards-compliant variant of JSON Web Tokens.
One option (which I mentioned in comment) is recording gameplay and replay it in secure environment.
The other thing is to randomly, or at some specified times, record some seemingly innocent data, which later can be used to validate it on server (like suddenly live goes from 1% to 100%, or score from 1 to 1000 which indicate cheat). With enough data it might just not be feasible for cheater to try to fake it. And then of course implement heavy banning :).
This very simple nonce changes every 1000 seconds (16 minutes)
and can be used for avoiding XSS where you are posting data to and from the same application. (For example if you are in a single page application where you are posting data via javascript. Note that you must have access to the same seed and nonce generator from the post and the receiving side)
function makeNonce($seed,$i=0){
$timestamp = time();
$q=-3;
//The epoch time stamp is truncated by $q chars,
//making the algorthim to change evry 1000 seconds
//using q=-4; will give 10000 seconds= 2 hours 46 minutes usable time
$TimeReduced=substr($timestamp,0,$q)-$i;
//the $seed is a constant string added to the string before hashing.
$string=$seed.$TimeReduced;
$hash=hash('sha1', $string, false);
return $hash;
}
But by checking for the previous nonce, the user will only be bothered if he waited more than 16.6 minutes in worst case and 33 minutes in best case. Setting $q=-4 will give the user at least 2.7 hours
function checkNonce($nonce,$seed){
//Note that the previous nonce is also checked giving between
// useful interval $t: 1*$qInterval < $t < 2* $qInterval where qInterval is the time deterimined by $q:
//$q=-2: 100 seconds, $q=-3 1000 seconds, $q=-4 10000 seconds, etc.
if($nonce==$this->makeNonce($seed,0)||$nonce==$this->makeNonce($seed,1)) {
//handle data here
return true;
} else {
//reject nonce code
return false;
}
}
The $seed, could be the any function call or user name, etc. used in the process.
It is not possible to prevent cheating. You can only make it more difficult.
If someone came here looking for a PHP Nonce Library: I recommend not using the first one given by ircmaxwell.
The first comment on the website describes a 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
If you are looking for a way to generate Nonces with a well-defined lifetime, have a look at NonceUtil-PHP.
Disclaimer: I am the author of NonceUtil-PHP

Categories