Math.random() is a fast pseudo-random generator built for things like animations, not security. Its output can be predicted from prior values, so using it to mint a token, session id, password-reset code, or OAuth state parameter lets an attacker guess valid ones. The fix is a single call to a cryptographic random source.
Why it's a problem
Anything that protects access needs to be unguessable. Because Math.random() is seeded and sequential under the hood, an attacker who observes a few outputs can narrow down or reproduce the sequence, then forge a reset code or a session token that the server accepts as legitimate. The value looks random to a human and is trivially reproducible to a machine.
The pattern
// predictable — do not use for anything security-sensitive const token = Math.random().toString(36).slice(2); const resetCode = String(Math.floor(Math.random() * 1000000));
The fix
import { randomUUID, randomInt, randomBytes } from "node:crypto";
const token = randomBytes(32).toString("hex"); // 256-bit token
const sessionId = randomUUID(); // unguessable id
const resetCode = String(randomInt(100000, 1000000)); // 6-digit, secureWhy AI tools write this
Math.random() is the first random function most models reach for because it is the one that appears most often in training data, in overwhelmingly non-security contexts. When the surrounding task is 'generate a token,' the assistant still pattern-matches to the familiar call, producing code that runs and looks correct but is not safe for the job.
The quick fix
- Replace Math.random() with crypto.randomBytes, crypto.randomUUID, or crypto.randomInt for any security-sensitive value.
- In the browser, use crypto.getRandomValues() instead.
- Audit tokens, session ids, reset codes, and OAuth state parameters specifically.