Stars: 8133
Forks: 129
Pull Requests: 98
Issues: 86
Watchers: 37
Last Updated: 2023-05-29 16:29:26
PHP 5.x support for random_bytes() and random_int()
License: MIT License
Languages: PHP, Shell
PHP 5.x polyfill for random_bytes()
and random_int()
created and maintained
by Paragon Initiative Enterprises.
Although this library should function in earlier versions of PHP, we will only consider issues relevant to supported PHP versions. If you are using an unsupported version of PHP, please upgrade as soon as possible.
Although this library has been examined by some security experts in the PHP community, there will always be a chance that we overlooked something. Please ask your favorite trusted hackers to hammer it for implementation errors and bugs before even thinking about deploying it in production.
Do not use the master branch, use a stable release.
For the background of this library, please refer to our blog post on Generating Random Integers and Strings in PHP.
If PHP cannot safely generate random data, this library will throw an Exception
.
It will never fall back to insecure random data. If this keeps happening, upgrade
to a newer version of PHP immediately.
With Composer:
# For libraries and frameworks that support PHP 5 but may be used by
# other software that only supports PHP 7:
composer require paragonie/random_compat:\>=2
# For software that explicitly needs PHP 5 support:
composer require paragonie/random_compat:\<9.99
Signed PHP Archive:
As of version 1.2.0, we also ship an ECDSA-signed PHP Archive with each stable release on Github.
.phar
, .phar.pubkey
, and .phar.pubkey.asc
files..phar.pubkey
(contained within the .asc
file) using the PGP public key for Paragon Initiative Enterprises..phar
and .phar.pubkey
files to the same directory.require_once "/path/to/random_compat.phar";
.phar
file;
the .pubkey
will not change (unless our signing key is ever compromised).Manual Installation:
require_once "/path/to/random_compat/lib/random.php";
The entrypoint should be lib/random.php
directly, not any of the other files in /lib
.
This library exposes the CSPRNG functions added in PHP 7 for use in PHP 5 projects. Their behavior should be identical.
try {
$string = random_bytes(32);
} catch (TypeError $e) {
// Well, it's an integer, so this IS unexpected.
die("An unexpected error has occurred");
} catch (Error $e) {
// This is also unexpected because 32 is a reasonable integer.
die("An unexpected error has occurred");
} catch (Exception $e) {
// If you get this message, the CSPRNG failed hard.
die("Could not generate a random string. Is our OS secure?");
}
var_dump(bin2hex($string));
// string(64) "5787c41ae124b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2eeac6f"
try {
$int = random_int(0, 255);
} catch (TypeError $e) {
// Well, it's an integer, so this IS unexpected.
die("An unexpected error has occurred");
} catch (Error $e) {
// This is also unexpected because 0 and 255 are both reasonable integers.
die("An unexpected error has occurred");
} catch (Exception $e) {
// If you get this message, the CSPRNG failed hard.
die("Could not generate a random int. Is our OS secure?");
}
var_dump($int);
// int(47)
When handling exceptions and errors you must account for differences between PHP 5 and PHP7.
The differences:
Error
works, so long as it is caught before Exception
.Exception
has different behavior, without previously catching Error
.Always catch Error
before Exception
.
try {
return random_int(1, $userInput);
} catch (TypeError $e) {
// This is okay, so long as `Error` is caught before `Exception`.
throw new Exception('Please enter a number!');
} catch (Error $e) {
// This is required, if you do not need to do anything just rethrow.
throw $e;
} catch (Exception $e) {
// This is optional and maybe omitted if you do not want to handle errors
// during generation.
throw new InternalServerErrorException(
'Oops, our server is bust and cannot generate any random data.',
500,
$e
);
}
If an Exception is thrown, then your operating system is not secure.
/dev/urandom
is readable.
/dev/urandom
from the host OSopen_basedir
, make sure /dev/urandom
is allowedThis library does not (and will not accept any patches to) fall back to an insecure random number generator.
If you're using a project that has a line like this in its composer.json
"require" {
...
"paragonie/random_compat": "~1.1",
...
}
...and then you try to add random_compat 2 (or another library that explicitly requires random_compat 2, such as this secure PHP encryption library), you will get a version conflict.
The solution is to get the project to update its requirement string to allow version 2 and above to be used instead of hard-locking users to version 1.
"require" {
...
- "paragonie/random_compat": "~1.1",
+ "paragonie/random_compat": ">=1",
...
}
Note: There is a special version called 9.99.99
which makes this
library do nothing, but is only installable on PHP 7.
If you're writing software (e.g. a library) that supports PHP 5, but may
be used by software that doesn't, you'll want to allow 9.99.99
to be
installed. The above diff is what you want.
Conversely, if you're writing software that (in and of itself) supports PHP 5, you do not want 9.99.99 to be installed, so you'll want to make this change instead:
"require" {
...
- "paragonie/random_compat": "~1.1",
+ "paragonie/random_compat": ">=1 <9.99",
...
}
To avoid installing "empty" version 9.99.99
you can add replace
section
in your root composer.json
:
"replace": {
"paragonie/random_compat": "9.99.99"
},
If you're using the PHP Archive (Phar) approach rather than Composer, and
you are getting an error message to the effect of "manifest read length
was {int1}
should be {int2}
", the Phar extension may not be enabled.
See this comment for specific guidance on how to fix this issue.
This project would not be anywhere near as excellent as it is today if it weren't for the contributions of the following individuals:
If your company uses this library in their products or services, you may be interested in purchasing a support contract from Paragon Initiative Enterprises.