Predictable Token
Also Known As
weak token
guessable token
insecure token generation
TL;DR
Tokens generated with md5(time()) or rand() are trivially guessable because their entropy source is predictable.
Explanation
Security tokens (password reset links, session IDs, API keys) must be unpredictable. Using md5(time()) produces only 86,400 distinct values per day — an attacker can brute-force all of them in seconds. rand() and mt_rand() are pseudorandom and their output can be predicted from a small number of observed values. The only correct source for security-sensitive random data is a CSPRNG: bin2hex(random_bytes(32)) in PHP.
Common Misconception
✗ Using a hash like md5(time()) produces an unpredictable token. Timestamps have limited entropy — an attacker who knows roughly when a token was generated can brute-force the millisecond-range space in seconds. Use random_bytes(32) instead.
Why It Matters
Guessable tokens — for sessions, CSRF, password resets, or API keys — allow an attacker to forge or brute-force a valid token without ever authenticating.
Common Mistakes
- Using rand() or mt_rand() which are seeded by time and guessable.
- Using MD5 or SHA1 of a timestamp — the timestamp is known or estimable.
- Sequential integer tokens for anything security-sensitive.
- Short tokens (less than 128 bits) that make brute force feasible even with secure randomness.
Code Examples
✗ Vulnerable
// Predictable — attacker can guess or brute-force
\$token = md5(\$userId . time());
\$token = uniqid(); // only 7 bytes of entropy
\$token = rand(); // seeded from time — predictable
✓ Fixed
// 32 bytes = 256 bits of entropy — computationally unguessable
\$token = bin2hex(random_bytes(32)); // 64 hex chars
// Store hash in DB, send raw token to user
\$stored = hash('sha256', \$token);
\$db->insert('password_resets', [
'user_id' => \$userId,
'token_hash' => \$stored,
'expires_at' => date('Y-m-d H:i:s', time() + 3600),
]);
// Verify with constant-time comparison:
if (!hash_equals(\$stored, hash('sha256', \$inputToken))) abort(400);
if (\$record->expires_at < now()) abort(400);
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
38
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Perplexity 8
Ahrefs 6
Amazonbot 5
Google 5
Unknown AI 2
ChatGPT 2
SEMrush 1
Also referenced
How they use it
crawler 29
Related categories
⚡
DEV INTEL
Tools & Severity
🔴 Critical
⚙ Fix effort: Low
⚡ Quick Fix
Use bin2hex(random_bytes(32)) for all tokens — password reset, email verification, API keys, CSRF; the 256-bit entropy makes brute force infeasible
📦 Applies To
PHP 7.0+
web
🔗 Prerequisites
🔍 Detection Hints
md5(uniqid()) md5(time()) rand() for tokens; sequential numeric token IDs; token derived from user ID + timestamp
Auto-detectable:
✓ Yes
semgrep
psalm
phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Low
✓ Auto-fixable
Fix: Low
Context: Line
Tests: Update
CWE-330
CWE-340
CWE-338