Description
Using a non-cryptographic pseudorandom number generator (PRNG), such as java.util.Random, Math.random(), ThreadLocalRandom, or SplittableRandom, for session IDs, tokens, or temporary passwords can make those values predictable. An attacker may infer the seed or internal state from observed values, then predict later outputs or narrow a guessing attack to take over sessions or accounts.
Potential impact
- Session hijacking: Predictable session IDs or cookies may allow impersonation.
- Account takeover: Guessing temporary passwords or login and email-verification tokens may give control of an account.
- Privilege abuse or escalation: Guessed access tokens or API keys may enable unauthorized actions.
- CSRF bypass: Predictable CSRF tokens may allow forged requests.
- Sensitive data exposure: Bypassing authentication or authorization may expose private information.
Remediation
- Use
java.security.SecureRandomfor security-sensitive values.- Example:
SecureRandom sr = new SecureRandom(); byte[] b = new byte[32]; sr.nextBytes(b);
- Example:
- Do not use weak PRNGs (
Random,Math.random(),ThreadLocalRandom, orSplittableRandom) for sessions, tokens, or security codes. - Do not set a fixed seed; let
new SecureRandom()obtain its default entropy. - Generate enough random bytes: At least 128 bits, preferably 192–256 bits, then encode them with URL-safe Base64.
- Give password-reset and email-verification tokens a short lifetime and make them single-use.
- Apply rate limits and failed-attempt limits to control guessing attempts.
Examples
Before
java
import javax.servlet.http.*;
import java.io.IOException;
import java.util.concurrent.ThreadLocalRandom;
public class WeakCookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
long id = ThreadLocalRandom.current().nextLong(); // Non-cryptographic PRNG
String value = Long.toHexString(id);
resp.addHeader("Set-Cookie", "SID=" + value + "; HttpOnly; Secure");
resp.getWriter().println("ok");
}
}
After
java
import javax.servlet.http.*;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Base64;
public class StrongCookieServlet extends HttpServlet {
private static final SecureRandom SR = new SecureRandom();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
byte[] token = new byte[32]; // 256 bits of entropy
SR.nextBytes(token);
String value = Base64.getUrlEncoder().withoutPadding().encodeToString(token);
resp.addHeader("Set-Cookie", "SID=" + value + "; HttpOnly; Secure; SameSite=Strict");
resp.getWriter().println("ok");
}
}
Explanation:
- Before:
ThreadLocalRandomis designed for performance, not cryptographic unpredictability. Cookies generated this way may be predictable or easier to guess, potentially enabling session hijacking. - After:
SecureRandomgenerates 256 random bits, which are encoded with URL-safe Base64. Sufficient entropy without a fixed seed reduces guessing risk. Token expiration and server-side validation still need to be implemented separately.