Giter VIP home page Giter VIP logo

random_compat's Introduction

random_compat

Build Status Scrutinizer Latest Stable Version Latest Unstable Version License Downloads

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.

Important

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.

Usability Notice

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.

Installing

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.

  1. Download the .phar, .phar.pubkey, and .phar.pubkey.asc files.
  2. (Recommended but not required) Verify the PGP signature of .phar.pubkey (contained within the .asc file) using the PGP public key for Paragon Initiative Enterprises.
  3. Extract both .phar and .phar.pubkey files to the same directory.
  4. require_once "/path/to/random_compat.phar";
  5. When a new version is released, you only need to replace the .phar file; the .pubkey will not change (unless our signing key is ever compromised).

Manual Installation:

  1. Download a stable release.
  2. Extract the files into your project.
  3. 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.

Usage

This library exposes the CSPRNG functions added in PHP 7 for use in PHP 5 projects. Their behavior should be identical.

Generate a string of random bytes

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"

Generate a random integer between two given integers (inclusive)

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)

Exception handling

When handling exceptions and errors you must account for differences between PHP 5 and PHP7.

The differences:

  • Catching Error works, so long as it is caught before Exception.
  • Catching Exception has different behavior, without previously catching Error.
  • There is no portable way to catch all errors/exceptions.

Our recommendation

Always catch Error before Exception.

Example

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

Troubleshooting

Exception: "Could not gather sufficient random data"

If an Exception is thrown, then your operating system is not secure.

  1. If you're on Windows, make sure you enable mcrypt.
  2. If you're on any other OS, make sure /dev/urandom is readable.
    • FreeBSD jails need to expose /dev/urandom from the host OS
    • If you use open_basedir, make sure /dev/urandom is allowed

This library does not (and will not accept any patches to) fall back to an insecure random number generator.

Version Conflict with [Other PHP Project]

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",
    ...
}

Version 9.99.99

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"
},

Manifest Read Length Error

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.

Contributors

This project would not be anywhere near as excellent as it is today if it weren't for the contributions of the following individuals:

Support Contracts

If your company uses this library in their products or services, you may be interested in purchasing a support contract from Paragon Initiative Enterprises.

random_compat's People

Contributors

asgrim avatar chillerdragon avatar connorvg avatar cs278 avatar cweagans avatar dd32 avatar glensc avatar ircmaxell avatar jedisct1 avatar jrfnl avatar juliangut avatar mmeyer2k avatar nicolas-grekas avatar oittaa avatar ossinkine avatar paragonie-scott avatar paragonie-security avatar redragonx avatar rugk avatar skyosev avatar slamdunk avatar stof avatar teohhanhui avatar theofidry avatar twistor avatar vinkla avatar wezzy avatar williamdes avatar xabbuh avatar zerocrates avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

random_compat's Issues

Call for Review before 1.0.6 is tagged?

Proposed for 1.0.6:

  • Add support for libsodium as an entropy source (suggested by @cweagans), if the extension is enabled (exposes getrandom(2) on modern Linux)
  • Relaxed open_basedir restrictions (suggested by @notr1ch) so, if /dev is listed in the open_basedir directive, it will still attempt to read from /dev/urandom
  • Allow random_bytes("3") without throwing an error

These are the specific changes to be released: 1.0.5...master

Afterwards, we can have a serious sober discussion about dropping OpenSSL and tagging 1.1.0.

documentation on setup

okay after trying out a lot I figured out how to install and use this.
I wanted just the random_int so I tried that one which didnt go nice since there was stuff from the other files that was needed so it would be nice to write at least a single sentence about that you need to copy all the files from /lib into ine folder and include/require the random.php which will make it work.

libSodium support broken?

It's been reported to WordPress Trac that the libSodium implementation is broken - https://core.trac.wordpress.org/ticket/35327

I'm hoping someone here will be able to confirm if that's the case. According to the libsodium documentation, it looks like it's being called correctly.

Usign libsodium for random bytes breaks plugin update in WP 4.4
Running WP 4.4 on
CentOS 7.2.1511
nginx 1.9.9
PHP 5.4.16
libsodium 1.0.5
php-pecl-libsodium 1.0.2
After updating to WP 4.4 plug-ins cannot be updated anymore.
Updates fail with
[06-Jan-2016 12:21:21 UTC] PHP Fatal error: Call to undefined function Sodium\randombytes_buf() in /var/www/verifyne.me/wp/wp-includes/random_compat/random_bytes_libsodium.php on line 69
The lines 66 and 69 state
\Sodium\randombytes_buf
whereas the following works for me
\Sodium::randombytes_buf

random_int() isn't outputting appropriate values

I ran the following code on my PHP 5.6.x installation:

require('random.php');

for ($i = 0; $i < 10; $i++) {
    $a = random_int(0,100);

    print "$a ";
}

And got the following output 1 6 5 4 3 4 1 3 6 4. Repeated iterations always output integers between 0 and 8 even though the specified maximum was 100.

no suitable CSPRNG 1.3.0

i'm getting error when upgrade to 1.3.0
Error : There is no suitable CSPRNG installed on your system on random.php file
my desktop ubuntu is PHP 7.0.4 and the Server PHP is 5.6 , i'm using random_compact in laravel 5.1
i can not fix it on Server so downgrade to 1.2.2
but on on my desktop everything is ok :)

Pure PHP PRNG?

Both Drupal and phpseclib try to get random bytes from a good source, and then if they can't, they fall back to a pure PHP PRNG.

The Drupal implementation looks like this:

      // If we couldn't get enough entropy, this simple hash-based PRNG will
      // generate a good set of pseudo-random bytes on any system.
      // Note that it may be important that our $random_state is passed
      // through hash() prior to being rolled into $output, that the two hash()
      // invocations are different, and that the extra input into the first one -
      // the microtime() - is prepended rather than appended. This is to avoid
      // directly leaking $random_state via the $output stream, which could
      // allow for trivial prediction of further "random" numbers.
      if (strlen($bytes) < $count) {
        // Initialize on the first call. The contents of $_SERVER includes a mix
        // of user-specific and system information that varies a little with
        // each page.
        if (!isset($random_state)) {
          $random_state = print_r($_SERVER, TRUE);
          if (function_exists('getmypid')) {
            // Further initialize with the somewhat random PHP process ID.
            $random_state .= getmypid();
          }
          $bytes = '';
        }

        do {
          $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
          $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
        } while (strlen($bytes) < $count);

The phpseclib implementation is here: https://github.com/phpseclib/phpseclib/blob/master/phpseclib/Crypt/Random.php#L116-L241 (too long to paste directly on this issue, and has a lot of dependencies on other parts of phpseclib).

My question is:

  1. Should random_compat provide a pure PHP PRNG for people that don't have any reasonable randomness sources?; and if so:
  2. Is there some existing implementation that would make sense to pull into random_compat

openssl_random_pseudo_bytes() does not appear to cryptographically secure

Although the PHP docs claim that openssl_random_pseudo_bytes() returns a "cryptographically strong" result as long as the $crypto_strong parameter is true, I checked the source and that does not appear to be the case. The openssl_random_pseudo_bytes() PHP function calls the RAND_psuedo_bytes() OpenSSL function, which the OpenSSL docs say should only be used for non-cryptographic purposes:

RAND_pseudo_bytes() has been deprecated. Users should use RAND_bytes() instead. RAND_pseudo_bytes() puts num pseudo-random bytes into buf. Pseudo-random byte sequences generated by RAND_pseudo_bytes() will be unique if they are of sufficient length, but are not necessarily unpredictable. They can be used for non-cryptographic purposes and for certain purposes in cryptographic protocols, but usually not for key generation etc.

It's quite possible I'm missing something here. If so, I apologize for wasting your time.

ERRATA.md incorrect advice about /dev/urandom

From ERRATA.md: "On Unix-based operating systems, it reads from /dev/urandom, which is the sane and correct thing to do."

This is not the sane and correct thing to do when a better API is available (getentropy(), getrandom(), the deprecated linux sysctl, or an arc4random() implementation backed by one of these - with /dev/urandom, opening of the device node can fail: out of file handles, program running in chroot.

In particular: if an attacker can cause too many FDs to be open, that would be Very Bad if you're relying on entropy from /dev/urandom.

SemVer

Since this is a BC break, semver dictates a minor version bump.

SemVer requires a major version bump for BC breaks doesn't it?

On Windows, CAPICOM is deprecated

I don't know if it's possible (I didn't managed yet), but since CAPICOM is deprecated since Vista, using $rng = new DOTNET('mscorlib', 'System.Security.Cryptography.RNGCryptoServiceProvider'); would be preferable.

OpenSSL on Windows: Require PHP 5.4.1?

openssl_random_pseudo_bytes() on Windows has been doing the correct thing since 5.4.1. One WordPress user has reported that it hangs even on PHP 5.3.5.

There are two ways to remedy this:

  1. Release a version 1.1.0 that kills OpenSSL, like @tom-- had suggested.
  2. Require 5.4.1 for this fallback to work.

Alternatively, we're going to be flooded with complaints about it making peoples' WordPress blogs slow/unresponsive when WP4.4 drops.

1.3 release

Can I just check when you plan to release 1.3 please. I am prepping security patches for laravel branches, old and new, to switch them over to ~1.3 (some of which still don't use this package at all yet).

mcrypt_create_iv() fails on Windows when env is empty

On Windows, the script below displays:
PHP Warning: mcrypt_create_iv(): Could not gather sufficient random data in Command line code on line 1

<?php

$pipes = array();
$env = array();

$p = proc_open('php -r "echo strlen(mcrypt_create_iv(1, MCRYPT_DEV_URANDOM));"', array(1 => array('pipe', 'w')), $pipes, getcwd(), $env);
echo stream_get_contents($pipes[1]), "\n";
proc_terminate($p);

The issue disappears when $env is set to null.

Version 1.1.0 Roadmap

  1. Expunge OpenSSL. This should only affect people with weird set-ups.

Does anything else need to change?

Split implementation into separate files

The all-in-one file approach is a necessity if all the environment dependencies are detected dynamically within one function, as it was originally. But since that has changed, I think using includes would (at least) make the code easier to follow.
I for one got kind of lost for a moment between all the conditional declarations of the same function, so I'd suggest the following:

byte_safe_strings.php
random_bytes_com_dotnet.php
random_bytes_dev_urandom.php
random_bytes_mcrypt.php
random_bytes_openssl.php
random_int.php

... you know what to do with them. :)

Exception: "Could not gather sufficient random data"

In a Symfony application the following error appears when attempting to go to any of several URIs:

request.CRITICAL: Uncaught PHP Exception Exception: "Could not gather sufficient random data" at /www2.projectmana.org/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php line 73 {"exception":"[object](Exception%28code: 0%29: Could not gather sufficient random data at /www2.projectmana.org/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php:73)"} []

The application is hosted on a site with freebsd, PHP 5.6.18. Symfony is 2.8.3. I am unable to reproduce the error on a Windows 10 system with same PHP , Symfony & application source code versions. A posting on Stackoverflow regarding this error is without any response as of this writing. Can you advise?

Comments on implementation.

More of a list of comments than a specific issue.

Feeling uncertain about your fallback order.

For systems that have it openssl_random_pseudo_bytes should be used first, it is both the quickest and well rounded provider of random. It uses it's own internal buffer/pool which draws multiple sources making it faster than direct file based random, but also draws from OS appropriate sources on windows.

Before 5.5.26 and 5.6.10 mcrypt_create_iv did not use file descriptor caching which makes it very slow for repeated use.

For random heavy applications, speed order for the majority of installations, openssl > /dev/*random > mcrypt. Can't comment on CAPICOM.

You might want to implement fd caching yourself. Opening and closing handles is pretty intensive work.

function random_bytes()
{
    static $fd;
    if (!$fd) {
        $fd = fopen(...

I'd say check for arandom, but on systems I care about, urandom is an alias of arandom anyway ;)

Consider moving the checks for supported implementations outside of the function? It seems wasteful to have to cascade through all of the existence checks on every call.

if (!function_exists('random_bytes')) {
    if (...) {
        function random_bytes($bytes) {
        ...
    } elseif ( ...) {
        function random_bytes($bytes) {
        ...

OpenSSL deprecation warning for v1.4.2

OpenSSL support was reintroduced with v1.4, and is used as an absolute last resort.

As an user of an security-related package depending on random_compat it would be nice to know if openssl_random_pseudo_bytes is actually used (e.g. v1.4 is used due to composer version constraints).

Triggering some deprecation notice seems reasonable to me – but maybe there are some better alternatives.

Should we buffer freads?

Should we disable buffering completely or would it be worthwhile to set the read buffer to some sane default value (i.e. 32 bytes instead of PHP's default 8192 bytes)?

Global namespace collision "Error"

As soon as I upgraded from ramsey/uuid 3.2 to 3.3 via composer, my servers started reporting the following PHP Fatal error:

PHP Fatal error: Call to undefined method Error::GetXML() in [script name]

Downgrading to 3.2, which also removes the prerequisite paragonie/random_compat v2.0.2, fixed the issue.

Issue appears to be in paragonie/random_compat extending Exception with the global name Error.

 // We can't really avoid making this extend Exception in PHP 5.
class Error extends Exception

See:
https://github.com/paragonie/random_compat/blob/master/lib/error_polyfill.php

Cheers.

Check for existence of mb_substr before mb_strlen usage

https://github.com/paragonie/random_compat/blob/master/lib/random.php#L301

if (!function_exists('RandomCompat_strlen')) {
    if (function_exists('mb_substr')) { # <= just here
        /**
         * [striped comment]
         */
        function RandomCompat_strlen($binary_string)
        {
            if (!is_string($binary_string)) {
                throw new InvalidArgumentException(
                    'RandomCompat_strlen() expects a string'
                );
            }
            return mb_strlen($binary_string, '8bit');
        }
    } else {
        // portion not necessary
    }
}

It's about RandomCompat_strlen ... just below the line 301, you only use mb_strlen, so, just a copy/paste error about the function_exists on mb_substr :)

My 2 cents

Is it really a compat package?

If this is indeed a compatibility package, it should behave exactly like PHP does (when possible of course). Therefore, it should check for (and give the highest priority to) /dev/arandom availability too.

Yes, /dev/urandom will simply redirect to /dev/arandom on current systems that have it and I made the same point on internals, but I also got a somewhat reasonable response on that.

On a side note, while I do agree that exceptions are better in this case, I'm quite sure that PHP itself currently returns FALSE (and emits an E_WARNING) in case that random_bytes() fails. You might want to work with them to change that while there's still time. :) Also ... I'd use InvalidArgumentException and RuntimeException instead of the base Exception class.

What to do about OpenSSL?

I'm stuck between several possible avenues:

  • Release a new version (v1.3.0 or most likely v2.0.0) that doesn't rely on OpenSSL at all
  • Create an OpenSSL-free fork, called secure_random
  • Possibly in either case allow LibreSSL/BoringSSL but definitely not OpenSSL

I'm interested in everyone's opinions here. The status quo is simply unacceptable.

lib/random.php should include a version number

For software like WordPress which bundles the library it's currently not easy to identify which version the library has. I propose to add the version number to the header in lib/random.php.

Consider Scrutinizer Integration

Scrutinizer is a great free (for Open Source projects) tool that can identify static code issues (security, docblocks, etc), have an extra run at your unit tests, and indentify areas where code could be improved (to reduce complexity or increase documentation), as well as areas the current unit testing suite doesn't actually cover (which on a related note you should consider having PHPUnit take a few extra seconds and generate a clover report https://github.com/easydigitaldownloads/Easy-Digital-Downloads/blob/master/.travis.yml#L33)

Adding it is as simple as tacking on a line to the end of your Travis file to send the Travis report to Scrutinizer, and adding a yml config file for it:
https://github.com/easydigitaldownloads/Easy-Digital-Downloads/blob/master/.travis.yml#L35
https://github.com/easydigitaldownloads/Easy-Digital-Downloads/blob/master/.scrutinizer.yml

Add a CHANGELOG

As an auditor I would like to see a CHANGELOG though to quickly see what a new version contains.

Could not generate a random string. Is our OS secure?

Could you please help me out with this error?

Could not generate a random string. Is our OS secure?

I have tried to Google for it, but can not find the answer.
I installed random_compat using composer.
I have linked it with:
require_once '../vendor/paragonie/random_compat/lib/random.php';

And used the following code:

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?");
}

Call for review before v1.0.0 is tagged

Once #7 can be safely closed (depends on two PRs on php-src), we should be 100% compatible with random_bytes() and random_int(). At such time, I intend to tag v1.0.0 as the first stable release.

Three questions:

  1. Does anyone have any objections?
  2. Would anyone like to request a delay in order to accommodate an in-depth review first?
  3. How confident are you that the current implementation is reasonably secure?

Thanks everyone for contributing to this effort to backport random_bytes() and random_int() for PHP 5 users, and thanks to everyone who worked on the CSPRNG for PHP 7.

random_bytes_dev_urandom has unusable performance on php 5.6

Due to stream_set_read_buffer() not doing what one would think it does, the first call to random_bytes()
in random_bytes_dev_urandom.php will always read 8 KiB data from /dev/urandom.

Unlike what http://php.net/manual/en/function.stream-set-read-buffer.php describes, the $buffer argument in php 5.6 is actually treated as a boolean flag - if it is zero the stream is unbuffered, if it is non-zero the stream is buffered, but the buffer size is never changed in any way...

Realized this after benchmarking a lumen app and seeing that the majority of CPU time was actually spent in the kernel generating random data.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.