VNX-342: Predictable Exact Value from Previous Values
Detects secrets that are the next value of a counter (sessionId++, ++lastToken, incrementAndGet(), atomic.AddInt64, next(counter), .succ) when the counter is named for a secret or its result is stored, returned or set as a cookie value of one. Java, Kotlin, Python, JavaScript/TypeScript, PHP, Ruby and Go.
| Field | Value |
|---|---|
| Rule ID | VNX-342 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Java, Kotlin, Python, JavaScript / TypeScript, PHP, Ruby, Go |
| CWE | CWE-342 |
| 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 | randomness predictable cwe-342 |
| Source | internal/sast/rules/vnx-342.rego |
Overview
VNX-342 maps to CWE-342: Predictable Exact Value from Previous Values. A session id, reset code or token that is the previous one plus one is not a secret: a user who receives their own value knows the value issued just before and the one that will be issued next.
The rule reports an increment (++x, x++, x += 1, x + 1, incrementAndGet()/getAndIncrement()/addAndGet(), Go atomic.AddInt64/AddUint64 and .Add(1), Python next(counter), Ruby .succ/.next) of a variable named as a counter or previous value (count, ctr, seq, last, prev, next, serial, latest, max, id, num) or for a secret, when:
- the incremented variable is itself named for a secret (
$_SESSION['last_session_id']++,captchaId++), or - its result is stored under, returned as or set as the cookie value of a secret (token, password, session id, reset/confirmation/invite code, OTP, CSRF, API key) on the same line, or
- a standalone increment is followed within four lines by a use of the variable as a secret, or
- the result is assigned to a variable that reaches a secret within six lines.
What Gets Flagged
// FLAGGED: the session cookie is a counter
$_SESSION['next_session_id']++;
setcookie("appSession", $_SESSION['next_session_id']);
// FLAGGED
public String newSessionId() {
return Long.toString(counter.incrementAndGet());
}
String resetToken = String.valueOf(++lastToken);
# FLAGGED
session_id = str(next(_ids))
// FLAGGED
id := atomic.AddInt64(&sessionCounter, 1)
return strconv.FormatInt(id, 10)
What Is Not Flagged
- Anonymous loop variables (
i++,n += 1) and loop headers. - Counters used as nonces, IVs, JWT ids or message sequence numbers: they need uniqueness, not secrecy. HOTP/HMAC over a counter.
- Counts, indexes, attempts and other bookkeeping names (
tokenCount++,$_SESSION['login_attempts']++,this.tokenPos++), order and request ids. - A line that also draws from a CSPRNG, test files, and the pattern in comments or string literals.
A secret that mixes a counter with the clock is also reported by VNX-341.
Remediation
- Generate each secret independently with a CSPRNG:
secrets.token_urlsafe(),crypto.randomBytes(),random_bytes(),SecureRandom,crypto/rand,SecureRandom.hex. - Keep sequential ids for database keys and ordering, and never hand them out as credentials.
- For confirmed false positives, add a
# vulnetix-ignore: VNX-342comment on the line.
References
- CWE-342: Predictable Exact Value from Previous Values
- OWASP Session Management Cheat Sheet: Session ID entropy
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-342
// nosec vnx-342
Or record an ignore rule: vulnetix ignore add --rule vnx-342 --reason "...". See Suppressing findings.