How to create and use nonces

I am launching a website, and there is a scoring system that gives you points for the number of times you play the game.

It uses hashing to prove the integrity of the HTTP request for scoring so that users can’t change anything, however, as I was afraid, it could happen that someone realized that they don’t need to change it, they just needed to get a high score, and duplicate the http request, headers and all.

Previously, I was forbidden to defend against this attack, because it was considered unlikely. However, now that this has happened, I can. The HTTP request is taken from a flash game, and then php is checked and php brings it to the database.

I am sure that nonces will solve the problem, but I am not quite sure how to implement it. What is the normal and safe way to configure a nonce system?

+60
php cryptography actionscript-3 nonce
Nov 10 2018-10-10
source share
5 answers

This is actually quite easy to do ... There are several libraries to do this for you:

  1. PHP Nonce Library
  2. Openid nonce library

Or, if you want to write your own, it's pretty simple. Using the WikiPedia page as a starting point, in pseudocode:

On the server side, you need two functions called by the client

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 makeRandomString function really just needs to return a random number or string. The better the randomness, the better the protection ... Also note that since it is passed directly to the hash function, implementation details do not matter from request to request. The client version and server version must not match. In fact, the only bit that must match 100% is the hash function used in hash('sha512', $nonce. $cnonce. $data); ... Here is an example of a fairly 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; } 
+59
Nov 10 2018-10-10
source share

Nonces is a gang of worms.

No, indeed, one of the motivations for several CAESARs was to create an authenticated encryption scheme, preferably based on a stream cipher, that is resistant to nonce reuse. (Reusing nonce with AES-CTR, for example, destroys the privacy of your message to the extent that the student can decrypt it for the first year.)

There are three main schools of thought with noses:

  • In symmetric-key cryptography: use an incrementing counter, ignoring its reuse. (This also means using a separate counter for the sender and receiver.) This requires stateful programming (i.e., somewhere the nonce value is stored, so each request does not start with 1 ).
  • States of random random. Create a random nonce and then remember it for confirmation later. This is the strategy used to defeat CSRF attacks , which sounds closer to what is offered here.
  • Large random characters without attacks. Given a robust random number generator, you can almost guarantee that you never repeat nonce twice in your life. This is the strategy used by NaCl for encryption.

So, keeping in mind, the main questions to ask are:

  • Which of the above schools of thought is most important for the problem you are trying to solve?
  • How do you generate nonce?
  • How do you check nonce?

Creating Nonce

The answer to question 2 for any random nonce is to use CSPRNG. For PHP projects, this means one of:

  • random_bytes() for projects with PHP 7+
  • paragonie / random_compat , PHP 5 polyfill for random_bytes()
  • ircmaxell / RandomLib , which is a Swiss army knife of utilities of randomness, which in most projects that deal with randomness (for example, resetting a password) should consider using instead of its own

These two are morally equivalent:

 $factory = new RandomLib\Factory; $generator = $factory->getMediumStrengthGenerator(); $_SESSION['nonce'] [] = $generator->generate(32); 

and

 $_SESSION['nonce'] []= random_bytes(32); 

Incompatibility Check

Stateful

Simple and recommended statements:

 $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]); 

Remember to replace array_search() with a database search or memcached, etc.

Stateless (dragons will be here)

This is a complex problem: you need to somehow prevent repeated attacks, but your server has general amnesia after every HTTP request.

The only sensible solution would be to authenticate the expiration date / time to minimize the effectiveness of the 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('Ymd\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 notice that this is basically a non-standard version of JSON Web Tokens .

+19
Mar 01 '16 at 5:08
source share

One option (which I mentioned in a comment) is to record the gameplay and play it in a secure environment.

Another thing is to randomly or at a certain time record some seemingly innocent data that can later be used to check it on the server (for example, suddenly live is from 1% to 100% or estimated from 1 to 1000 which indicate cheat ) If there is enough data, it may not be appropriate for a fraudster to try to fake it. And then, of course, implement a heavy prohibition :).

+1
Nov 10 2018-10-10
source share

This very simple one-time number changes every 1000 seconds (16 minutes) and can be used to prevent XSS when you publish data to and from one application. (For example, if you are in a single-page application in which you publish data using javascript. Note that you must have access to the same seed and one-time number generator from the post and recipient)

 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 the previous one-time number, the user will only be worried if he waits more than 16.6 minutes in the worst case and 33 minutes in the best case. Setting $ q = -4 will give the user a minimum of 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; } } 

$ Seed can be any function call or username, etc. Used in the process.

0
Mar 09 '19 at 17:18
source share

Unable to prevent cheating. You can only make it more complicated.

If someone came here looking for Nonce PHP library: I do not recommend using the first one provided by ircmaxwell .

The first comment on the site describes the design flaw:

A one-time number is suitable for one specific time window, i.e. The closer a user comes to the end of these windows, the less time he or she needs to submit a form, possibly less than one second

If you are looking for a way to generate disposable numbers with a well-defined lifetime, take a look at NonceUtil-PHP .

Disclaimer: I am the author of NonceUtil-PHP

-one
Apr 29 '13 at 11:33
source share



All Articles