Laravel Insecure Random Numbers: How to Replace rand() and mt_rand() with Cryptographic Alternatives
Using rand() or mt_rand() for tokens, passwords, or security decisions creates predictable values that attackers can guess or reproduce.
Computers are deterministic machines — they produce the same output given the same input. True randomness requires external entropy, so programming languages implement pseudo-random number generators (PRNGs), which produce sequences that appear random but are determined by an initial seed value. PHP's rand() and mt_rand() use PRNGs seeded from predictable sources like the current time. An attacker who can observe a few outputs from these functions can mathematically reconstruct the internal state and predict every future output.
The attack against predictable random values is called PRNG state reconstruction. If a password reset token is generated with md5(mt_rand()), an attacker who requests a password reset can observe when the request was made (from the email timestamp), use the timestamp as the seed range, and generate every possible mt_rand() output for a small time window. The number of possibilities is small enough to brute-force against the application's reset endpoint within minutes. The same applies to session identifiers, OTP codes, and API keys generated with predictable randomness.
Real-world exploits of this class have been documented. Researchers found that several PHP applications generated password reset tokens using md5(microtime()), making them trivially predictable — the seed space for a 1-second window is only 1,000,000 possible values. PHP 7 introduced random_bytes() and random_int() as official cryptographically secure alternatives, drawing entropy from the operating system's CSPRNG (/dev/urandom on Linux). Laravel's Str::random() uses random_bytes() internally, making it safe for security-sensitive usage.
The Problem
PHP's rand() and mt_rand() functions use predictable pseudo-random number generators (PRNGs). An attacker who observes a few outputs can predict future values — and in some cases, reconstruct past values. If you use these functions to generate password reset tokens, API keys, session identifiers, CSRF tokens, or any security-sensitive value, attackers can predict them and gain unauthorized access. PHP provides cryptographically secure alternatives that should always be used instead.
How to Fix
-
1
Find insecure random usage in your code
Search for rand() and mt_rand() in security-sensitive contexts:
grep -rn 'rand(\|mt_rand(\|array_rand(\|shuffle(' app/ --include='*.php'Look specifically for usage in: - Token generation - Password/PIN creation - OTP codes - Nonce or salt generation - Random filename generation for uploads - Lottery/selection logic with financial impact
-
2
Replace with cryptographically secure alternatives
Use these replacements:
// Random integers // BEFORE: rand(100000, 999999) // AFTER: random_int(100000, 999999);// Random strings/tokens // BEFORE: md5(mt_rand()) // AFTER: Str::random(40); // Laravel helper (uses random_bytes) bin2hex(random_bytes(20)); // Raw PHP// Random bytes // BEFORE: mt_rand() based // AFTER: random_bytes(32);// Shuffling arrays securely // BEFORE: shuffle($array) // AFTER: Use a Fisher-Yates shuffle with random_int() -
3
Use Laravel helpers for common patterns
Laravel provides secure random generation out of the box:
// Random string use Illuminate\Support\Str; Str::random(40); // 40-char random string Str::uuid(); // UUID v4 Str::orderedUuid(); // Time-ordered UUID// Token generation $token = hash('sha256', Str::random(60));// Unique filenames $filename = Str::uuid() . '.' . $file->extension();All of these use random_bytes() internally.
How to Verify
Search for remaining insecure usage:
grep -rn 'mt_rand(\|\brand(' app/ --include='*.php' | grep -v '// safe\|// non-security'
Run php artisan stackshield:scan --check=SS040 to verify.
Prevention
Use Str::random(), random_int(), or random_bytes() for all security-sensitive randomness. rand() and mt_rand() are acceptable only for non-security uses like UI element positioning or test data generation. Add a code review rule to flag rand()/mt_rand() usage.
Frequently Asked Questions
Is mt_rand() safe for non-security purposes?
Yes. For non-security uses like randomizing display order, generating test data, or adding visual variation, mt_rand() is fine. The key distinction is: will predictable values cause a security issue? If not, mt_rand() is acceptable and faster.
Is Str::random() cryptographically secure?
Yes. Laravel's Str::random() uses random_bytes() internally, which is cryptographically secure. It is safe for generating tokens, passwords, and other security-sensitive values.
Is uniqid() safe to use for generating tokens?
No. uniqid() is based on microtime() and produces a value that is almost entirely predictable from the current timestamp. It is not suitable for tokens, session IDs, or any security-sensitive identifier. Use Str::random(40) or bin2hex(random_bytes(20)) instead, both of which draw from the operating system's CSPRNG.
How should I generate a secure 6-digit OTP or PIN code?
Use random_int(100000, 999999) for a 6-digit numeric OTP. random_int() uses a CSPRNG and is safe for security-sensitive purposes. For a zero-padded 6-digit code: str_pad(random_int(0, 999999), 6, "0", STR_PAD_LEFT). Never use rand(), mt_rand(), or time-based generation for OTPs — the predictability makes them trivially brute-forceable.
What about PHP's openssl_random_pseudo_bytes() function?
openssl_random_pseudo_bytes() is cryptographically secure when the second $strong output parameter returns true, but PHP 7+ added random_bytes() as the preferred, simpler alternative that is always cryptographically strong without requiring the $strong parameter check. Use random_bytes() in new code — it is simpler to use correctly and available in all PHP versions that Laravel supports.
Related Guides
Laravel Weak Password Hashing: How to Replace md5 and sha1 with bcrypt
Using md5() or sha1() for password hashing is trivially crackable. Laravel uses bcrypt by default — make sure your application does too.
Laravel APP_KEY Security: How to Generate, Rotate, and Protect Your Encryption Key
A missing, short, or committed APP_KEY compromises session encryption, signed URLs, and all data encrypted with Crypt. Generate a strong key and keep it out of Git.
Laravel Session Security: Fix Insecure Cookie Config (Secure, HttpOnly, SameSite)
Laravel session cookies missing Secure, HttpOnly, or SameSite flags? Fix your config/session.php to prevent session hijacking, cookie theft, and CSRF attacks.
Is your Laravel app exposed right now?
34% of Laravel apps we scan have at least one critical issue. Most teams don't find out until something breaks. Our free scan checks your live application in under 60 seconds.