Improve openssl_random_pseudo_bytes() View as Markdown

Implemented (PHP 7.4)
Authors
Sammy Kaye Powers sammyk@php.net
Date
2018-10-19
Version
0.1
Implementation
https://github.com/php/php-src/pull/3649
Source
wiki.php.net/rfc/improve-openssl-random-pseudo-bytes

Votes

Make openssl_random_pseudo_bytes() fail closed closed
Yes
30
No
0
30 voters
ab Yes 2018-11-06 16:28
ashnazg Yes 2018-11-07 15:13
bukka Yes 2018-11-04 17:00
bwoebi Yes 2018-11-06 10:41
colinodell Yes 2018-11-07 13:38
dm Yes 2018-11-10 13:20
dragoonis Yes 2018-11-05 09:44
emir Yes 2018-11-11 11:12
galvao Yes 2018-11-03 08:45
hywan Yes 2018-11-05 09:34
irker Yes 2018-11-07 10:43
jhdxr Yes 2018-11-11 03:09
kalle Yes 2018-11-04 17:02
kelunik Yes 2018-11-03 09:31
kguest Yes 2018-11-05 12:34
lex Yes 2018-11-13 17:16
mcmic Yes 2018-11-05 09:21
mgocobachi Yes 2018-11-06 23:51
mike Yes 2018-11-08 14:27
narf Yes 2018-11-03 18:26
ocramius Yes 2018-11-03 20:30
petk Yes 2018-11-15 09:59
pmmaga Yes 2018-11-06 11:29
pollita Yes 2018-11-02 20:04
ramsey Yes 2018-11-08 15:15
rasmus Yes 2018-11-06 20:21
sammyk Yes 2018-11-10 04:28
svpernova09 Yes 2018-11-06 13:48
trowski Yes 2018-11-16 17:28
zimt Yes 2018-11-09 11:51
Deprecate the usage of the $crypto_strong parameter closed
Yes
12
No
12
24 voters
ab No 2018-11-06 16:26
ashnazg Yes 2018-11-07 15:13
bukka No 2018-11-04 16:59
bwoebi No 2018-11-06 10:41
dm No 2018-11-10 13:20
emir No 2018-11-11 11:12
galvao Yes 2018-11-03 08:44
hywan No 2018-11-05 09:34
jhdxr Yes 2018-11-11 03:09
kalle No 2018-11-04 17:02
kelunik Yes 2018-11-03 09:31
kguest Yes 2018-11-05 12:35
lex Yes 2018-11-13 17:17
mcmic Yes 2018-11-05 09:21
nikic No 2018-11-03 10:21
ocramius No 2018-11-03 20:31
pmmaga No 2018-11-06 11:29
pollita Yes 2018-11-02 20:07
ramsey Yes 2018-11-08 15:17
rasmus No 2018-11-06 20:22
sammyk Yes 2018-11-10 04:28
svpernova09 Yes 2018-11-06 13:48
trowski Yes 2018-11-16 17:29
zimt No 2018-11-09 11:51

Discussions on the mailing list

Alias openssl_random_pseudo_bytes() to php_random_bytes_throw()
by Sammy Kaye Powers · 5 messages
[RFC] Improve openssl_random_pseudo_bytes()
by Sammy Kaye Powers · 8 messages
[RFC] [VOTE] Improve openssl_random_pseudo_bytes()
by Sammy Kaye Powers · 8 messages
PHP 7.4 BC break with openssl_random_pseudo_bytes()
by Christian Schneider · 16 messages

RFC text

Copied from wiki.php.net, last synchronized Oct 13, 2026 11:51 UTC

Introduction

The openssl_random_pseudo_bytes() function is a wrapper for OpenSSL's RAND_bytes CSPRNG. CSPRNG implementations should always fail closed, but openssl_random_pseudo_bytes() fails open pushing critical fail checks into userland. It also has an unnecessary second parameter that confuses the usage of the API.

The Fail-Open Problem

The openssl_random_pseudo_bytes() function fails open which means code like this:

function genCsrfToken(): string
{
    return bin2hex(openssl_random_pseudo_bytes(32));
}

...could return an empty string. This forces the developer to do their own checks and fail closed in userland.

function genCsrfToken(): string
{
    $bytes = openssl_random_pseudo_bytes(32);
    if (false === $bytes) {
        throw new \Exception('CSPRNG error');
    }
    return bin2hex($bytes);
}

A quick search in GitHub reveals very little checking of the return value of openssl_random_pseudo_bytes() in the wild.

CSPRNG implementations should always fail closed.

The Confusing API Problem

There is also a confusing pass-by-reference param $crypto_strong. According to the docs:

It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter.

This forces yet another check in userland to determine if the bytes are strong enough for crypto. The usage of this parameter is unnecessary since openssl_random_pseudo_bytes() already returns false on failure and the implementation doesn't allow returning a string of bytes while also setting $crypto_strong to false.

The API is unnecessarily confusing making it easy to get it wrong. The above userland example isn't even correct according to the docs. The correct usage in userland would actually be:

function genCsrfToken(): string
{
    $strong = false;
    $bytes = openssl_random_pseudo_bytes(32, $strong);
    if (false === $bytes || false === $strong) {
        throw new \Exception('CSPRNG error');
    }
    return bin2hex($bytes);
}

This redundant check is confusing for developers and the documentation does not properly describe the behavior of the implementation.

Proposal

To fix the fail-open problem, we simply throw an \Exception (just like ''random_bytes()'' does). This is the Correct Behavior™️ for any CSPRNG implementation.

To fix the the confusing-api problem, we should deprecate the usage of the second $crypto_strong parameter and just make it always set the value to true. In PHP 8.0 we'd completely remove the second parameter and upgrade the function's ZPP to ZEND_PARSE_PARAMS_THROW causing the following fatal error when attempting to send in the second argument.

PHP Fatal error: Uncaught ArgumentCountError: openssl_random_pseudo_bytes() expects exactly 1 parameter, 2 given

Backward Incompatible Changes

False-checks on the return value of openssl_random_pseudo_bytes() will do nothing since the function fails closed. Usage of $crypto_strong will generate errors.

Proposed PHP Version(s)

PHP 7.4

RFC Impact

Unaffected PHP Functionality

The openssl_random_pseudo_bytes() function will continue to use OpenSSL's RAND_bytes CSPRNG.

Proposed Voting Choices

Requires a 2/3 majority

Voting started 2018-11-02 @ 19:30 UTC and will close sometime around 2018-11-16 @ 19:30 UTC

Vote #1: Make openssl_random_pseudo_bytes() fail closed

Vote: Make openssl_random_pseudo_bytes() fail closed — Yes: 30 · No: 0 (closed)

Vote #2: Deprecate the usage of the $crypto_strong parameter

Vote: Deprecate the usage of the $crypto_strong parameter — Yes: 12 · No: 12 (closed)

Patches and Tests

  1. The patch & tests

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature
  4. a link to the language specification section (if any)

References

  1. Initial discussion
  2. Under-discussion announcement

Rejected Features

  1. The original ping to @internals suggested aliasing openssl_random_pseudo_bytes() to random_bytes(), but this was not received well so that idea got put in the bin.