Server Implementation
API keys, OTPs, CSRF tokens, or retry nonces are generated from `random.random()` / `Math.random()` — a statistical PRNG, not a cryptographic one. Outputs are predictable across processes. Session IDs specifically are scoped to MCP-267, which has narrower identifier-name detection; the two rules do not co-fire on the same value.
`random` in Python and `Math.random` in JavaScript are Mersenne-Twister / xorshift PRNGs — excellent for simulations, catastrophic for security tokens. Their internal state is small enough that an attacker who sees a few outputs can reconstruct the state and predict all future outputs. The fix is simple: use `secrets.token_urlsafe()` / `crypto.randomBytes()` / `crypto.getRandomValues()` whenever the output grants access.
MCP servers often mint their own session IDs, per-tool API keys for client callbacks, or OTPs for confirmation flows. A `random.randint(0, 999999)` OTP is brute-forceable; a `Math.random()` token is predictable. Because MCP tools rarely expose themselves to human-facing review, these bugs ship without anyone noticing until exploitation.
import random |
import string |
@server.tool() |
def create_session() -> str: |
# random is seeded from time/PID; attacker-predictable after a few observations |
return "".join(random.choice(string.ascii_letters) for _ in range(24)) |
import secrets |
@server.tool() |
def create_session() -> str: |
# secrets uses os.urandom — CSPRNG, always suitable for tokens |
return secrets.token_urlsafe(24) |
We flag `random.random` / `random.randint` / `random.choice` / `random.getrandbits` and `Math.random` calls whose result is assigned to, or used in, a variable whose name matches `/token|secret|session|nonce|otp|csrf|password|api_?key|salt|iv/i`. We do not flag uses in clearly non-security contexts (e.g. test data generation).
See the full threat catalog for every documented detection.
MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.
Scan now