v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
HIGH AI PREVALENCE: HIGH auth.flow.insecure-random

Math.random() produces a value whose name marks it as security-sensitive.

Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.

Why this matters

The value is assigned to (or computed for) a token, a CSRF value, an OAuth state, a session id, a nonce, or a verification code. Math.random() is NOT cryptographically secure and is predictable enough for an attacker with enough samples to recover the seed.

Use crypto.randomBytes(N) (Node) or crypto.getRandomValues() (browser) and base64url-encode the result.

OWASP ASVS V2.5: all security-sensitive random values must come from a CSPRNG.

VULNERABLE
vulnerable.ts
// ruleid: auth.flow.insecure-random
const csrfToken = Math.random();

// ruleid: auth.flow.insecure-random
const sessionId = Math.random().toString(36);

// ruleid: auth.flow.insecure-random
const otpCode = Math.random();

// ruleid: auth.flow.insecure-random
const resetCode = Math.random().toString(36);

// ruleid: auth.flow.insecure-random -- the most common insecure-token idiom
const apiToken = Math.random().toString(36).slice(2);

// ruleid: auth.flow.insecure-random
const verifyToken = Math.random().toString(36).substring(2);

// ruleid: auth.flow.insecure-random -- Math.floor wrapper
const sessionIdNum = Math.floor(Math.random() * 1e9);

// ruleid: auth.flow.insecure-random -- var, not const/let
var legacyToken = Math.random().toString(36);

class Session {
  resetToken = '';
  rotate() {
    // ruleid: auth.flow.insecure-random -- member assignment
    this.resetToken = Math.random().toString(36).slice(2);
  }
}

export { csrfToken, sessionId, otpCode, resetCode, apiToken, verifyToken, sessionIdNum, legacyToken, Session };
SAFE
safe.ts
import { randomBytes } from 'node:crypto';

// ok: auth.flow.insecure-random
export const csrfToken = randomBytes(32).toString('base64url');

// ok: auth.flow.insecure-random -- non-security value
export const animationDelay = Math.random() * 200;

// ok: auth.flow.insecure-random -- non-security value
export const pickColor = Math.random();

// These names contain security-word *substrings* but are not secrets, and use
// Math.random() legitimately — regression guards against substring matches.
// ok: auth.flow.insecure-random -- "barcode" is not a security token
export const barcode = Math.random().toString(36);
// ok: auth.flow.insecure-random -- "zipcode" is not a security token
export const zipcode = Math.random().toString().slice(2, 7);
// ok: auth.flow.insecure-random -- camelCase, unrelated UI state
export const gameState = Math.random();

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

// oauthlint-disable-next-line auth.flow.insecure-random -- <reason>

References

https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html ↗https://cwe.mitre.org/data/definitions/338.html ↗