How To Create A Simple Nonce in PHP
Tags: php, webdev, programming
Published: 2022-06-02 19:51:03

A Nonce is a **number or a token used only once**. 

You can use Nonce in your pages or forms to add an extra layer of security to your App and one of its features is to **differentiate humans from bots**.

Today, I will show you how to create a simple Nonce in PHP, and how you can validate it.

## LET'S BEGIN

First, we will create a **class** that will have **private and public methods**  that will generate and validate our Nonce based on the secret hash which we will define.

### OUR CLASS

```php
session_start();

define('NONCE_SECRET', 'CEIUHET745T$^&%&%^gFGBF$^');

class Nonce {
/**
	 * Generate a Nonce. 
	 * 
	 * The generated string contains four tokens, separated by a colon.
	 * The first part is the salt. 
	 * The second part is the form id.
	 * The third part is the time until the nonce is invalid.
	 * The fourth part is a hash of the three tokens above.
	 * 
	 * @param $length: Required (Integer). The length of characters to generate
	 * for the salt.
	 * 
	 * @param $form_id: Required (String). form identifier.
	 * 
	 * @param $expiry_time: Required (Integer). The time in minutes until the nonce 
	 * becomes invalid. 
	 * 
	 * @return string the generated Nonce.
	 *
	 */

	/**
	 * Verify a Nonce. 
	 * 
	 * This method validates a nonce
	 *
	 * @param $nonce: Required (String). This is passed into the verifyNonce
	 * method to validate the nonce.
	 *  
	 * @return boolean: Check whether or not a nonce is valid.
	 * 
	 */
}
```
Now we need to set up a **private function (method)** that will generate random characters which is our **salt**, up to the characters that we will specify in its **length** parameter.

Let us make this method a public function, as we test run our code.

```php
public function generateSalt($length = 10){
    //set up random characters
    $chars='1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
    //get the length of the random characters
	$char_len = strlen($chars)-1;
    //store output
	$output = '';
    //iterate over $chars
    while (strlen($output) < $length) {
         /* get random characters and append to output 
               till the length of the output is greater than the length provided */
	    $output .= $chars[ rand(0, $char_len) ];
	}
    //return the result
    return $output;
}
```
Here's the full code

```php
session_start();

define('NONCE_SECRET', 'CEIUHET745T$^&%&%^gFGBF$^');

class Nonce {

    public function generateSalt($length = 10){
    	//set up random characters
    	$chars='1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
    	//get the length of the random characters
		$char_len = strlen($chars)-1;
    	//store output
		$output = '';
    	//iterate over $chars
    	while (strlen($output) < $length) {
        	/* get random characters and append to output till the length of the output 
             is greater than the length provided */
	    	$output .= $chars[ rand(0, $char_len) ];
		}
    	//return the result
    	return $output;
	}
}
```
Let us use the new keyword to create an instance of the class, and invoke the **generateSalt()** method.

```php
$nonce = new Nonce();

var_dump($nonce->generateSalt(22));

```
This is our result, notice that the random characters were generated up to the length we specified.

![nonce](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o7zx7d62nfzib3uj7aj8.png) 

Now we need another private method that will take advantage of $_SESSION, to store the generated Nonces using the form ID.

```php
private function storeNonce($form_id, $nonce){
    //Argument must be a string
    if (is_string($form_id) == false) {
        throw new InvalidArgumentException("A valid Form ID is required");
	}
    //group Generated Nonces and store with md5 Hash
    $_SESSION['nonce'][$form_id] = md5($nonce);
    return true;
}

```

Next, we need to create another method that will **hash our salt, our secret, and a specific time of expiry** as tokens. Then we will generate a nonce by separating each token with a colon and store it in a session variable using the **storeNonce** method.


```php
public function generateNonce($length = 10, $form_id, $expiry_time){
    //our secret
    $secret = NONCE_SECRET;
    //secret must be valid. You can add your regExp here
    if (is_string($secret) == false || strlen($secret) < 10) {
			throw new InvalidArgumentException("A valid Nonce Secret is required");
	}
    //generate our salt
    $salt = self::generateSalt($length);
    //convert the time to seconds
    $time = time() + (60 * intval($expiry_time));
    //concatenate tokens to hash
    $toHash = $secret.$salt.$time;
    //send this to the user with the hashed tokens
    $nonce = $salt .':'.$form_id.':'.$time.':'.hash('sha256', $toHash);
    //store Nonce
    self::storeNonce($form_id, $nonce);
    //return nonce
    return $nonce;
}
```
Here's the full code

```php
session_start();

define('NONCE_SECRET', 'CEIUHET745T$^&%&%^gFGBF$^');

class Nonce {
    //generate salt
    public function generateSalt($length = 10){
    	//set up random characters
    	$chars='1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
    	//get the length of the random characters
		$char_len = strlen($chars)-1;
    	//store output
		$output = '';
    	//iterate over $chars
    	while (strlen($output) < $length) {
        	/* get random characters and append to output till the length of the output 
             is greater than the length provided */
	    	$output .= $chars[ rand(0, $char_len) ];
		}
    	//return the result
    	return $output;
	}
    //store Nonce
    private function storeNonce($form_id, $nonce){
        //Argument must be a string
        if (is_string($form_id) == false) {
            throw new InvalidArgumentException("A valid Form ID is required");
        }
        //group Generated Nonces and store with md5 Hash
        $_SESSION['nonce'][$form_id] = md5($nonce);
        return true;
    }
    //hash tokens and return nonce
    public function generateNonce($length = 10, $form_id, $expiry_time){
        //our secret
        $secret = NONCE_SECRET;

        //secret must be valid. You can add your regExp here
        if (is_string($secret) == false || strlen($secret) < 10) {
			throw new InvalidArgumentException("A valid Nonce Secret is required");
	    }
    	//generate our salt
        $salt = self::generateSalt($length);
        //convert the time to seconds
        $time = time() + (60 * intval($expiry_time));
        //concatenate tokens to hash
        $toHash = $secret.$salt.$time;
        //send this to the user with the hashed tokens
        $nonce = $salt .':'.$form_id.':'.$time.':'.hash('sha256', $toHash);
        //store Nonce
        self::storeNonce($form_id, $nonce);
        //return nonce
        return $nonce;
	}
}

```
So let us invoke the method **generateNonce()**, and pass in **5** as the first argument (this will be used to generate our salt), **form_login** as the second argument (this will be the form we wish to use the nonce on), and **10** as the third or last argument (this will be how long our nonce will last for in minutes).

```php
$nonce = generateNonce();

var_dump($nonce->generateNonce(5, "form_login", 10));
var_dump($_SESSION);
```
Here's our nonce and its stored value in the session.

![nonce](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/izwor5dy8guflcrh3znn.png) 

You can see that we have each token separated by a colon in the generated nonce, and we have it stored in the session by hashing it with** md5()**.

Now we need to code a public method that will verify a nonce and return a boolean (true or false), depending on whether or not the nonce is valid.

Before we code this method, you need to understand that:
- Our nonce is stored in $_SESSION 
- Our tokens are separated by a colon ($salt : $form_id : $time : $hash)
- We will use the same secret used to generate the Nonce, to verify it. 

```php
public function verifyNonce($nonce){
    //our secret
    $secret = NONCE_SECRET;
    //split the nonce using our delimeter : and check if the count equals 4
    $split = explode(':', $nonce);
    if(count($split) !== 4){
        return false;
    }
    
    //reassign variables
    $salt = $split[0];
    $form_id = $split[1];
    $time = intval($split[2]);
    $oldHash = $split[3];
    //check if the time has expired
    if(time() > $time){
        return false;
    }

    /* Nonce is proving to be valid, continue ... */

    //check if nonce is present in the session
    if(isset($_SESSION['nonce'][$form_id])){
        //check if hashed value matches
        if($_SESSION['nonce'][$form_id] !== md5($nonce)){
            return false;
        }
    }else{
         return false;
    }
    
    //check if the nonce is valid by rehashing and matching it with the $oldHash
    $toHash = $secret.$salt.$time;
    $reHashed = hash('sha256', $toHash);
    //match with the token
    if($reHashed !== $oldHash){
        return false;
    }
    /* Wonderful, Nonce has proven to be valid*/
    return true;
}
```
So we have a few conditions in the method above that checks the nonce. This method will only return **true** when the checks are successful and **false** when one or more checks fail.

Here are the checks:

- If the tokens are not complete, the nonce is invalid
- If Nonce is not stored in the session, the Nonce is invalid
- If Nonce is stored but the value does not match, the Nonce is invalid
- If the time has elapsed, the nonce is invalid
- If there's an alteration to the hash, the nonce is invalid

Let us create a nonce and verify it to confirm if our class works and we can replace the **public function generateSalt()** with a **private function** so that it can only be accessed within our class.

Here's the full code

```php

session_start();

define('NONCE_SECRET', 'CEIUHET745T$^&%&%^gFGBF$^');

class Nonce {
    //generate salt
    private function generateSalt($length = 10){
    	//set up random characters
    	$chars='1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM';
    	//get the length of the random characters
		$char_len = strlen($chars)-1;
    	//store output
		$output = '';
    	//iterate over $chars
    	while (strlen($output) < $length) {
        	/* get random characters and append to output till the length of the output 
             is greater than the length provided */
	    	$output .= $chars[ rand(0, $char_len) ];
		}
    	//return the result
    	return $output;
	}
    //store Nonce
    private function storeNonce($form_id, $nonce){
        //Argument must be a string
        if (is_string($form_id) == false) {
            throw new InvalidArgumentException("A valid Form ID is required");
        }
        //group Generated Nonces and store with md5 Hash
        $_SESSION['nonce'][$form_id] = md5($nonce);
        return true;
    }
    //hash tokens and return nonce
    public function generateNonce($length = 10, $form_id, $expiry_time){
        //our secret
        $secret = NONCE_SECRET;

        //secret must be valid. You can add your regExp here
        if (is_string($secret) == false || strlen($secret) < 10) {
			throw new InvalidArgumentException("A valid Nonce Secret is required");
	    }
    	//generate our salt
        $salt = self::generateSalt($length);
        //convert the time to seconds
        $time = time() + (60 * intval($expiry_time));
        //concatenate tokens to hash
        $toHash = $secret.$salt.$time;
        //send this to the user with the hashed tokens
        $nonce = $salt .':'.$form_id.':'.$time.':'.hash('sha256', $toHash);
        //store Nonce
        self::storeNonce($form_id, $nonce);
        //return nonce
        return $nonce;
	}
   public function verifyNonce($nonce){
        //our secret
        $secret = NONCE_SECRET;
        //split the nonce using our delimeter : and check if the count equals 4
        $split = explode(':', $nonce);
        if(count($split) !== 4){
            return false;
        }
    
        //reassign variables
        $salt = $split[0];
        $form_id = $split[1];
        $time = intval($split[2]);
        $oldHash = $split[3];
        //check if the time has expired
        if(time() > $time){
            return false;
        }
        /* Nonce is proving to be valid, continue ... */

        //check if nonce is present in the session
        if(isset($_SESSION['nonce'][$form_id])){
            //check if hashed value matches
            if($_SESSION['nonce'][$form_id] !== md5($nonce)){
                return false;
            }
        }else{
             return false;
        }
    
        //check if the nonce is valid by rehashing and matching it with the $oldHash
        $toHash = $secret.$salt.$time;
        $reHashed = hash('sha256', $toHash);
        //match with the token
        if($reHashed !== $oldHash){
            return false;
        }
        /* Wonderful, Nonce has proven to be valid*/
        return true;
    }
}
```
So I generated a nonce and passed it as a string to the method and it worked perfectly!

To verify your nonces, note that If **true** is returned, it means that the nonce is still valid, but If **false** is returned, it means that the nonce is invalid.

So our function works perfectly! In order to use this nonce, you need to place the class into a file and include that file into your page using the **require()** keyword.

Then create a new instance of the class, and generate a nonce

```php
//include nonce 
require_once('nonce.php');
//create new instance of the class
$nonce = new Nonce();
//generate nonce
$myToken = $nonce->generateNonce(25, 'form_login', 10);
//verify nonce
$result = $nonce->verifyNonce($myToken)
//display result
var_dump($result);
```
So there you have it

![nonce](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6rmt4q3r83n4o5xqxdi0.png) 

---

### AMAZING ISN'T IT?

![spongeBob](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ixkv77xeihg4kqgu00y5.png)

Make it suit your project by using a very strong secret.

You have reached the end of my article.

## EXTRA

I recently launched a JavaScript package that validates your HTML forms using validation rules, regular expressions, and form input attributes.

I will appreciate it if you spared a few seconds to check it out.

{% embed https://www.producthunt.com/posts/octavalidate %}

Thank You