DEV Community

Cover image for Why Cursor Keeps Using Math.random() for Session Tokens (CWE-330)
Charles Kern
Charles Kern

Posted on • Originally published at safeweave.dev

Why Cursor Keeps Using Math.random() for Session Tokens (CWE-330)

TL;DR

  • AI editors default to Math.random() for tokens because that's what nearly every tutorial and StackOverflow answer uses
  • Math.random() is not cryptographically secure, so tokens built from it can be predicted or brute-forced
  • Swap in crypto.randomBytes or crypto.randomUUID for anything security-sensitive

I was reviewing a password reset flow Cursor had scaffolded for a side project last week. The endpoint looked clean. Route, validation, database call, all fine. Then I got to the token generation line and stopped.

function generateResetToken() {
  return Math.random().toString(36).substring(2); // not safe for this
}
Enter fullscreen mode Exit fullscreen mode

This function generates the token that gets emailed to a user to reset their password. Anyone who can predict or narrow down the output space can hijack an account without ever touching the password field.

The Problem With Math.random()

Math.random() is a pseudo-random number generator built for things like shuffling an array or picking a random UI color. It is not built to resist an attacker. Its internal state can, in some engines, be recovered from a handful of outputs, and even without that, the output space from .toString(36).substring(2) is small enough to be practical to brute-force for a reset token that's valid for an hour.

This isn't a Cursor-specific bug. Claude Code and Copilot reproduce the exact same pattern when you ask for "a function to generate a random token" or "a random ID for this record."

Why This Keeps Happening

Go search "generate random string javascript." The first ten results almost all use Math.random(). They're not wrong for what they're demonstrating. If you're building a demo, a placeholder key for a React list, or a coin flip, Math.random() is completely fine.

The problem is that AI editors don't know the difference between a random string for a UI key and a random string that guards account access. They've seen millions of examples of the first pattern used to solve both problems, because most tutorials never separate the two use cases. The model reproduces the most common pattern it's seen for "generate random string," and that pattern was optimized for simplicity, not security.

The Fix

const crypto = require('crypto');

function generateResetToken() {
  return crypto.randomBytes(32).toString('hex'); // safe
}

// or, for a simple unique ID:
const token = crypto.randomUUID(); // safe
Enter fullscreen mode Exit fullscreen mode

Python has the same trap with random.randint() or random.choice() for tokens. Use the secrets module instead:

import secrets
token = secrets.token_hex(32)  # safe
Enter fullscreen mode Exit fullscreen mode

The rule of thumb: if the value grants access, proves identity, or gets used as a secret, it needs to come from a cryptographically secure source. If it's just for display or non-security uniqueness, Math.random() is still fine.

I've been running SafeWeave for this. It hooks into Cursor and Claude Code as an MCP server and flags insecure randomness patterns like this one before I move on. A grep for Math.random() near words like "token," "password," or "session" catches most of it too, if you'd rather do it by hand.

Top comments (0)