VNX-336: Same Seed in Pseudo-Random Number Generator (PRNG)
Detects a PRNG seeded with a constant (a literal, an ALL_CAPS constant or a variable assigned one) when its output is security-relevant, and any seeded SecureRandom or ChaCha8. Python, Java, Kotlin, Go, C, C++, PHP, Ruby and JavaScript.
| Field | Value |
|---|---|
| Rule ID | VNX-336 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Python, Java, Kotlin, Go, C, C++, PHP, Ruby, JavaScript / TypeScript |
| CWE | CWE-336 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | prng seed cwe-336 |
| Source | internal/sast/rules/vnx-336.rego |
Overview
VNX-336 maps to CWE-336: Same Seed in Pseudo-Random Number Generator (PRNG). A generator seeded with a constant produces the same sequence on every run, so anyone who reads the code, or observes one run, knows every value it will produce.
The rule reports a constant seed (a number, a string or byte literal, an ALL_CAPS constant, or a variable assigned one of those within eight lines above) passed to random.seed()/random.Random(), new Random()/Random()/setSeed()/new SecureRandom(bytes), Go rand.Seed/NewSource/NewPCG/NewChaCha8, C/C++ srand/srandom/srand48/seed48 and the <random> engine constructors and .seed(), PHP mt_srand/srand and the Random\Engine constructors, Ruby srand/Random.new/Random.srand, and JavaScript seedrandom().
A seeded SecureRandom or ChaCha8 is always reported. A general-purpose PRNG is reported only when its output matters: the seed call sits in a function named for a secret, the generator is stored under a secret’s name, or the same file draws from a PRNG into a token, password, key, salt, nonce, OTP, CSRF or session value.
What Gets Flagged
# FLAGGED: fixed seeds, and the file draws a reset token from the generator
random.seed(1337)
rng = random.Random(42)
def make_reset_token():
return "".join(random.choices(string.ascii_letters, k=32))
// FLAGGED: generator stored under a secret's name
private static final Random TOKEN_RNG = new Random(9001L);
// FLAGGED: a seeded SecureRandom is deterministic
sr.setSeed("acme-static-seed".getBytes(StandardCharsets.UTF_8));
// FLAGGED: ChaCha8 is fully determined by its seed
return rand.NewChaCha8([32]byte([]byte("0123456789abcdef0123456789abcdef")))
What Is Not Flagged
- Reproducible seeds in simulations, data generators, games and benchmarks where nothing in the file is a secret. numpy and other library generators are not examined.
- Unseeded or OS-seeded generators:
random.seed(),new SecureRandom(),rand.New(rand.NewSource(time...))(clock seeds are VNX-337). - Test files, and calls in comments or string literals.
Seeds narrowed to a small range are VNX-339.
Remediation
- Generate secrets with a CSPRNG that seeds itself from the operating system:
secrets,SecureRandom(never seeded by hand),crypto/rand,random_bytes(),crypto.randomBytes(),SecureRandom.hex. - Keep fixed seeds for reproducible simulations and tests only, in code that produces no secrets.
- For confirmed false positives, add a
# vulnetix-ignore: VNX-336comment on the line.
References
- CWE-336: Same Seed in Pseudo-Random Number Generator (PRNG)
- OWASP Cryptographic Storage Cheat Sheet: Secure random number generation
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-336
// nosec vnx-336
Or record an ignore rule: vulnetix ignore add --rule vnx-336 --reason "...". See Suppressing findings.