paragonie/random_compat · critical · Exception

Environment misconfiguration: /dev/urandom cannot be read.

Error message

Environment misconfiguration: /dev/urandom cannot be read.

What it means

random_compat's /dev/urandom backend only opens /dev/urandom when running on a Unix-like OS (DIRECTORY_SEPARATOR === '/'). If is_readable('/dev/urandom') returns false, the library throws because it cannot access the system CSPRNG and must not fall back to weak randomness. This indicates a broken or hostile environment rather than bad arguments.

Solutions

  1. Ensure /dev/urandom exists and is readable by the PHP user; in containers mount dev (e.g. add /dev/urandom device or run with proper device cgroup).
  2. Check and fix open_basedir restrictions: add /dev/urandom to open_basedir in php.ini.
  3. Fix filesystem permissions on /dev/urandom (typically 0666 root:root) or the chroot so the device node is present.
  4. Upgrade PHP to >= 7.0 for native random_bytes() and verify random_compat still selects a working backend (check which file was loaded).

Example fix

// before (fails in restricted env)
$token = random_bytes(32);
// after
if (!is_readable('/dev/urandom')) {
    error_log('CSPRNG unavailable: /dev/urandom not readable; check open_basedir/chroot/container /dev mount');
}
$token = random_bytes(32);
Defensive patterns

Strategy: try-catch

Validate before calling

if (DIRECTORY_SEPARATOR === '/' && !is_readable('/dev/urandom')) {
    throw new RuntimeException("Environment misconfiguration: /dev/urandom unavailable");
}

Try / catch

try {
    $bytes = random_bytes(32);
} catch (Exception $e) {
    if (strpos($e->getMessage(), '/dev/urandom') !== false) {
        // fix env (mount /dev, open_basedir, permissions) — do not use weak RNG
        throw new RuntimeException('CSPRNG environment misconfiguration', 0, $e);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling random_bytes() on a Unix-like system where /dev/urandom does not exist, is not readable by the PHP process (permissions, chroot/jail without /dev, hardened open_basedir), or the filesystem lacks the device node (some containers/minimal images).

Common situations: Docker/alpine or minimal chroot images without /dev mounted or without the urandom device; hosting environments using open_basedir restricting access to /dev; running PHP as a user without read permission on /dev/urandom; misconfigured chroot for PHP-FPM.

Related errors


AI-assisted analysis of paragonie/random_compat@b5d188cc9d (2026-09-13). Data as JSON: /api/errors/6a5e8a8819444e68. Report an issue: GitHub.

Appendix: source

Thrown at lib/random_bytes_dev_urandom.php:69

        static $fp = null;

        /**
         * This block should only be run once
         */
        if (empty($fp)) {
            /**
             * We don't want to ever read C:\dev\random, only /dev/urandom on
             * Unix-like operating systems. While we guard against this
             * condition in random.php, it doesn't hurt to be defensive in depth
             * here.
             *
             * To that end, we only try to open /dev/urandom if we're on a Unix-
             * like operating system (which means the directory separator is set
             * to "/" not "\".
             */
            if (DIRECTORY_SEPARATOR === '/') {
                if (!is_readable('/dev/urandom')) {
                    throw new Exception(
                        'Environment misconfiguration: ' .
                        '/dev/urandom cannot be read.'
                    );
                }
                /**
                 * We use /dev/urandom if it is a char device.
                 * We never fall back to /dev/random
                 */
                /** @var resource|bool $fp */
                $fp = fopen('/dev/urandom', 'rb');
                if (is_resource($fp)) {
                    /** @var array<string, int> $st */
                    $st = fstat($fp);
                    if (($st['mode'] & 0170000) !== 020000) {
                        fclose($fp);
                        $fp = false;
                    }
                }

View on GitHub (pinned to b5d188cc9d)