Generating cryptographically secure tokens

PhpSecurityOpensslToken

Php Problem Overview


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?

Php Solutions


Solution 1 - Php

Here is the correct solution:

$token = bin2hex(openssl_random_pseudo_bytes(16));

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

Solution 2 - Php

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.

Solution 3 - Php

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.

Solution 4 - Php

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

Solution 5 - Php

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfireView Question on Stackoverflow
Solution 1 - PhpfireView Answer on Stackoverflow
Solution 2 - PhpmjsaView Answer on Stackoverflow
Solution 3 - PhpimichaelmiersView Answer on Stackoverflow
Solution 4 - PhpMacroManView Answer on Stackoverflow
Solution 5 - PhpDaro from PolandView Answer on Stackoverflow