Generate string securely random in PHP

Asked

Viewed 4,937 times

5

How, using the PHP language, generate a string, preferably with configurable size, random enough to be used in routines dealing with cryptography and that for safety reasons cannot be insufficiently random?

If you propose more than one solution, preferably clarify which one you think is more random, possibly with external additional references. Solutions involving more than one source of randomness generator are also welcome, in particular if accompanied by explanation of advantages, such as reducing problems with an operating system explicitly changed by an NSA of life to generate less chaos when producing randomness.

Even if two people propose the same code, if one of these explains better or proves with real examples the dispersion of values, it will be considered as the best answer. It is not enough to copy and paste code, you should discuss why it is a good solution.

Solutions that directly access an operating system resource, using for example shell_exec, although they are not desirable, they are also welcome to explain in what context they would be advantageous like older PHP versions or that they do not have a specific PHP module that would allow using a more appropriate routine.

4 answers

5


When it comes to encryption/security issues, the ideal is to use tools made even for this.

An example in PHP is openssl_random_pseudo_bytes, a function that was made to generate random numbers (with a configurable number of bytes).

$bytes = openssl_random_pseudo_bytes($length, $strong);
if (!$strong) {
    //Tratar o erro
    exit();
}
$hex = bin2hex($bytes);

This library is not always available, which can be problematic.

On Linux, the /dev/random is a special "file" that allows you to extract random bytes. The /dev/urandom is very similar to /dev/random, although they have slightly different behaviors when the computer is "without entropy", that is, it has no more data to search for as a source of random numbers.

Supposedly, as far as is known, use this file for security purposes.

$file = fopen('/dev/urandom', 'r');
if (!$file) {
    // Falhou
    exit();
}
$bytes = fread($file, $length);
fclose($file);
$hex = bin2hex($bytes);

This second solution has the disadvantage of not being available on Windows.

What doesn’t fit

rand and mt_rand They weren’t designed for cryptography, so they don’t fit. The function mt_rand is described by the documentation as "better" than the rand, but still has this warning:

Caution

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 openssl_random_pseudo_bytes() Instead.

In English:

Care

This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a value cryptographically secure, consider using openssl_random_pseudo_bytes in time of this.

The function uniqid also does not serve (see the notice in the documentation itself, the notice is not available in all languages):

This Function does not create Random nor unpredictable string. This Function must not be used for security purposes. Use cryptographically Secure Random Function/Generator and cryptographically Secure hash functions to create unpredictable Secure ID.

In Portuguese:

This function does not create random or unpredictable strings. This function cannot be used for security purposes. Use number generators safe random and cryptographically secure hash functions for create secure unpredictable Ids.

The hash(md5, sha, etc.) of a value that is not random enough for cryptographic purposes is still not cryptographically secure.

2

PHP 7 or higher.

You can use the random_bytes(), it is cryptographically secure and uses the /dev/urandom:

$numero_de_bytes = 12;
$random = random_bytes($numero_de_bytes);

To have a given in hexadecimal you will have to use, in this case, the bin2hex:

echo bin2hex($random);

This will have as output 24 humanly understandable letters random. ;)


Libsodium possibly already embedded in PHP 7.2 with another syntax.

The Libsodium is a library PECL which is one of the safest and which must be inserted into PHP 7.2, with change in syntax.

$numero_de_bytes = 12;
$random = \Sodium\randombytes_buf($numero_de_bytes);

Already the Libsodium has a function that replaces the bin2hex original PHP because it is vulnerable to attacks side-Channel, then use:

echo \Sodium\bin2hex($random);

Another option is to use:

echo \Sodium\bin2hex(
          \Sodium\randombytes_buf($numero_de_bytes)
     );

1

Already exists in PHP 5.3.0 or higher specific routine for this.

http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

Use Openssl to generate the strings, with one parameter being the size and the other a boolean that returns True if it is considered secure enough to use with encryption.

Reference to the Openssl website on how it works.

http://wiki.openssl.org/index.php/Random_Numbers

And if you want to test you can take the results of the convert to binary function and run a statistical test suite to validate how random the generated values are using NIST’s Cryptographic Toolkit.

  • maybe you should put the code of this routine in your answer, it is easier for people to see and if one day the link fails your answer will still be valid.

  • I agree with you that you could have set an example, but you didn’t have time to write and test a routine, which could become outdated with time and updates in the PHP API. The link is directly to the PHP API page, from where it is not allowed to reproduce the content of the site, so you could not copy and paste the example here. But I thank you for your suggestions and I will take it into consideration for my next responses.

  • @Math There is another license problem, all content placed on the site is automatically licensed by cc by-sa 3.0, unless the code was made by me, it could only post some ready code on the web if the license was compatible. What in the case of the PHP site is not, because the content of their site is Copyright and is not allowed to copy all or part of it. Therefore, care is needed when posting third party code here.

1

There are several ways to generate "random" strings, and generally the key issue in algorithms is the Seed which will be the source of the entropy and the algorithm that will generate the random string.

A very simple way to generate a pseudo-random string in PHP is:

$size = 2; // até 40
$seed = time(); // time() só para exemplo!
echo substr(sha1($seed), 40 - min($size,40));

The advantage is that it is quite simple, but it is not so random. But it solves if it is for a simple application.


Another solution is to use the mt_rand() function that uses the algorithm Mersenne Twister as a pseudo-randomness generator. With this function you can generate pseudo-random strings with higher quality. However, for some cases it is can be quite slow (compared to Rand() it uses LCG).

There are other variations of MT (better even), SFMT (uses SSE) and MTGP (optimized for Gpus), but I don’t know if there are implementations of them for PHP.

You can use the following to generate your MT strings like this:

function random_str_mt($size) 
{
    $keys = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));

    $key = '';
    for ($i = 0; $i < ($size+10); $i++) 
    {
        $key .= $keys[array_rand($keys)];
    }

    return substr($key, 0, $size);
}

echo random_str_mt(20);

In the above example, substr() breaks a piece of the generated string to improve the quality of the result.


For encryption the best solution is to use own functions for this purpose. If you use PGP the function openssl_random_pseudo_bytes. For example:

function random_str_ossl($size) 
{
    return bin2hex(openssl_random_pseudo_bytes($size));
}

echo random_str_ossl(20);

In Windows, the call from openssl_random_pseudo_bytes is considerably slow in some versions of PHP (I think the newer ones have fixed this).


Another alternative is to use the dev/urandom or /dev/random. As I’ve never tested, I’ll just leave this one link.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.