fire
fire

Reputation: 21531

Generating cryptographically secure tokens

In order to generate a 32 character token for access to our API we currently use:

$token = md5(uniqid(mt_rand(), true));

I have read that this method is not cryptographically secure as it's based on the system clock, and that openssl_random_pseudo_bytes would be a better solution as it would be harder to predict.

If this is the case, what would the equivalent code look like?

I presume something like this, but I don't know if this is right...

$token = md5(openssl_random_pseudo_bytes(32));

Also what length makes sense that I should pass to the function?

Upvotes: 93

Views: 89553

Answers (5)

Daro from Poland
Daro from Poland

Reputation: 17

Reliable passwords You can only make from ascii characters a-zA-Z and numbers 0-9. To do that best way is using only cryptographically secure methods, like random_int() or random_bytes() from PHP7. Rest functions as base64_encode() You can use only as support functions to make reliability of string and change it to ASCII characters.

mt_rand() is not secure and is very old. From any string You must use random_int(). From binary string You should use base64_encode() to make binary string reliable or bin2hex, but then You will cut byte only to 16 positions (values). See my implementation of this functions. It uses all languages.

Upvotes: -1

MacroMan
MacroMan

Reputation: 2458

Another option is using RandomLib from ircmaxell (https://github.com/ircmaxell/RandomLib)

Install: composer require ircmaxell/random-lib

Example medium strength

$factory = new Factory();
$factory->getMediumStrengthGenerator()->generateString(32);

Upvotes: 0

mjsa
mjsa

Reputation: 4399

If you want to use openssl_random_pseudo_bytes it is best to use CrytoLib's implementation, this will allow you to generate all alphanumeric characters, sticking bin2hex around openssl_random_pseudo_bytes will just result in you getting A-F (caps) and numbers.

Replace path/to/ with where you put the cryptolib.php file (you can find it on GitHub at: https://github.com/IcyApril/CryptoLib)

<?php
  require_once('path/to/cryptolib.php');
  $token = IcyApril\CryptoLib::randomString(16);
?>

The full CryptoLib documentation is available at: https://cryptolib.ju.je/. It covers a lot of other random methods, cascading encryption and hash generation and validation; but it's there if you need it.

Upvotes: 10

fire
fire

Reputation: 21531

Here is the correct solution:

$token = bin2hex(openssl_random_pseudo_bytes(16));

# or in php7
$token = bin2hex(random_bytes(16));

Upvotes: 203

imichaelmiers
imichaelmiers

Reputation: 3519

If you have a cryptographically secure random number generator, you don't need to hash it's output. In fact you don't want to. Just use

$token  = openssl_random_pseudo_bytes($BYTES,true)

Where $BYTES is however many bytes of data you want. MD5 has a 128bit hash, so 16 bytes will do.

As a side note, none of the functions you call in your original code are cryptographically safe, most are harmful enough that using just one would break be insecure even if combined with secure other functions. MD5 has security issues(though for this application they may not be relevant). Uniqid not just doesn't generate cryptographically random bytes by default (since it uses the system clock), the added entropy you pass in is combined using a linear congruent generator, which is not cryptographically secure. In fact, it probably means one could guess all your API keys given access to a few of them even if they had no idea the value of your server clock. Finally, mt_rand(), what you use as the extra entropy, is not a secure random number generator either.

Upvotes: 9

Related Questions