Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Davva's avatar
Level 10

How to generate secret codes / tokens

I have a booking system and want to generate a cancellation code, say 6 characters or so, that can be used to cancel a booking:

For example: /bookings/cancel/Hk7D#A

The code should be as short as possible, still be reasonably secure.

On the same note, I would also like to be able to generate API tokens. These are obviously a lot longer, but perhaps there is a function/package that can be used for both?

Ideally, one should be able to something like this: $code = new SecretCode(6); // would produce a secret code of 6 chars.

Any suggestions?

0 likes
14 replies
ax3lst's avatar

I do:

$code = str_random(10); //would produce a secret code of 10 chars.
5 likes
JoshWilley's avatar
Level 5

This is a class that I created which I have found useful:

class UUID {

    /**
     * @var
     */
    public $prefix;

    /**
     * @var
     */
    public $entropy;

    /**
     * @param string $prefix
     * @param bool $entropy
     */
    public function __construct($prefix = '', $entropy = false)
    {
        $this->uuid = uniqid($prefix, $entropy);
    }

    /**
     * Limit the UUID by a number of characters
     * 
     * @param $length
     * @param int $start
     * @return $this
     */
    public function limit($length, $start = 0)
    {
        $this->uuid = substr($this->uuid, $start, $length);

        return $this;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->uuid;
    }
} 

It's actually pretty damn flexible too. For example:

$code = new UUID;

return $code; // Will return something like 53ef6b2ae4da1
$code = new UUID('secret_');

return $code; // Will return something like secret_53ef6b2ae4da1
$code = new UUID;

return $code->limit(6); // Will return something like 53ef6b

Please note that this should not be used for anything that needs to be cryptographically secure. PHP's "uniqid" function is basically just random characters generated from the "microtime" function.

4 likes
mrterryh's avatar

The problem with code generation is this: what happens when you generate a code that is already stored in the database? Obviously, it's highly unlikely that something like this will happen, but it's good to have a precaution in place, so here's what I usually do.

Make a query in a generateCode() function once it's been generated, and count the rows. If a code already exists, I recursively call the function. If not, simply return the code that has been generated.

JoshWilley's avatar

@mrterryh - It depends on how the code/token is generated, but 99% of the time, I would say that duplicate codes would not be a concern. Especially in the example I posted above, since PHP's uniqid() function uses microtime(), the precision you get is down to the microsecond. Add some random salt as a prefix, turn on the entropy and you're pretty much guaranteed to get a unique code every time.

However, to err on the side of caution, just set the column to unique in the database, throw an exception if an integrity violation is returned from the database, listen for that exception and generate a new code.

This is especially simple if you're using Laravel/Eloquent.

1 like
mrterryh's avatar

@JoshWilley ah, didn't read your code so didn't realise you were using uniqid(). I simply use the Laravel str_random() function so, as you can imagine, chances of duplicate codes are a little higher.

Davva's avatar
Level 10

Great answers, thanks! That UUID function is exactly what I needed.

jimmy.puckett's avatar

@Davva, just to add a little to the mix, we use the ramsey/uuid library inside our Uuid Class for generating and working with RFC 4122 version 1, 3, 4, and 5 uUUID's. Then depending on the needs of the UUID we decide which version that we need. It is available on packagist...

composer require "rhumsaa/uuid=~2.7"
1 like
michaelmano's avatar

Pfff. $confirmation_code = str_random(12).''.$user->id.''.str_random(12); This way you cant really guess it, its always unique due to the users id being unique also.

Valorin's avatar

It's worth pointing out the warning on the uniqid() php.net manual page:

This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using random_int(), random_bytes(), or openssl_random_pseudo_bytes() instead.

It uses microtime() internally, so the random strings it generates are easily guessable and not very random. It shouldn't be used for anything remotely important that someone shouldn't be able to guess.

str_random() is a much better function to use, as it generates secure random strings of any length.

Note: Collisions may still happen (although the chance is very low), so check it's unique in your DB before saving it - or use a unique column index. :-)

jmp909's avatar

may seem like a silly question but I'm assuming str_random could theoretically generate a string with expletives in it?

In the past I have alternated letters and numbers, excluding any numbers that look like vowels etc (0,1, 8...) for any random code that is public facing

thanks J

BARNZ's avatar

While str_random() works fine, you could also use Laravels own token generating functions. This will generate a secure token same as whats used for things like password resets. Simply do:

use Illuminate\Support\Facades\Password;
$token = Password::getRepository()->createNewToken();

Will generate something like

785f616c4978a87ad65a899ed4133b358a4697649c55b0965a7ebb7486bd9801.

At the time of writing, Laravels token generation technique is:

hash_hmac('sha256', Str::random(40), <your-app-key>)

4 likes
bbouton's avatar

I needed this exact thing. I took some of the stuff here and implemented as follows:

<?php
namespace App\Helpers;

use Illuminate\Support\Facades\Password;

class UUID
{
    /**
     * generates random strings. Can generate unique strings by passing a
     * fully namespaced model class and field name to check against.
     * @param  integer $length     Length of string to be generated. max 64
     * @param  [type]  $modelClass fully namespaced model class to check unique
     * @param  [type]  $fieldName  field name to check unique in model
     * @return [type]              random/unique string of specified length
     */
    public static function generate($length=64, $modelClass = null, $fieldName=null)
    {
        $token = substr(Password::getRepository()->createNewToken(), 0, $length);

        if ($modelClass && $fieldName) {
            if ($modelClass::where($fieldName, '=', $token)->exists()) {
                //Model Found -- call self.
                self::generate($length, $modelClass, $fieldName);
            } else {
                //Model Not found. is uinque
                return $token;
            }
        } else {
            return $token;
        }
    }
}
1 like

Please or to participate in this conversation.