← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Predictable Token

Security CWE-338 OWASP A2:2021 CVSS 7.5 PHP 7.0+ Intermediate
debt(d5/e3/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5), semgrep/psalm/phpstan rules can flag rand()/mt_rand()/md5(uniqid()) patterns used as tokens, but it requires the security-focused ruleset, not default lint.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3), the quick_fix is to swap to bin2hex(random_bytes(32)) at each token generation site — straightforward replacement but typically needs touching every generation point.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3), token generation is usually centralized in a few helpers (password reset, CSRF, API keys); the bad choice doesn't gravitationally shape the rest of the codebase.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7), the misconception that md5(time()) or md5(uniqid()) is unpredictable is widespread and contradicts intuition — hashing feels like it adds security but timestamp entropy is trivially brute-forceable.

About DEBT scoring →

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

Added 15 Mar 2026
Edited 22 Mar 2026
Views 72
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 2 pings W 2 pings T 4 pings F 0 pings S 8 pings S 0 pings M 2 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Scrapy 15 Perplexity 8 Ahrefs 8 Amazonbot 6 Google 6 SEMrush 3 Unknown AI 2 ChatGPT 2 Claude 2 Meta AI 1 Qwen 1
crawler 52 crawler_json 2
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


✓ schema.org compliant