Pseudorandom number generators (PRNGs) use deterministic mathematical algorithms to produce a sequence of numbers with good statistical properties. However, the sequences of numbers produced fail to achieve true randomness. PRNGs usually start with an arithmetic seed value. The algorithm uses this seed to generate an output value and a new seed, which is used to generate the next value, and so on.
The Java API provides a PRNG, the java.util.Random
class. This PRNG is portable and repeatable. Consequently, two instances of the java.util.Random
class that are created using the same seed will generate identical sequences of numbers in all Java implementations. Seed values are often reused on application initialization or after every system reboot. In other cases, the seed is derived from the current time obtained from the system clock. An attacker can learn the value of the seed by performing some reconnaissance on the vulnerable target and can then build a lookup table for estimating future seed values.
Consequently, the java.util.Random
class must not be used either for security-critical applications or for protecting sensitive data. Use a more secure random number generator, such as the java.security.SecureRandom
class.
Noncompliant Code Example
This noncompliant code example uses the insecure java.util.Random
class. This class produces an identical sequence of numbers for each given seed value; consequently, the sequence of numbers is predictable.
import java.util.Random; // ... Random number = new Random(123L); //... for (int i = 0; i < 20; i++) { // Generate another random integer in the range [0, 20] int n = number.nextInt(21); System.out.println(n); }
Compliant Solution
This compliant solution uses the java.security.SecureRandom
class to produce high-quality random numbers:
import java.security.SecureRandom; import java.security.NoSuchAlgorithmException; // ... public static void main (String args[]) { SecureRandom number = new SecureRandom(); // Generate 20 integers 0..20 for (int i = 0; i < 20; i++) { System.out.println(number.nextInt(21)); } }
Compliant Solution (Java 8)
This compliant solution uses the SecureRandom.getInstanceStrong()
method, introduced in Java 8, to use a strong RNG algorithm, if one is available.
import java.security.SecureRandom; import java.security.NoSuchAlgorithmException; // ... public static void main (String args[]) { try { SecureRandom number = SecureRandom.getInstanceStrong(); // Generate 20 integers 0..20 for (int i = 0; i < 20; i++) { System.out.println(number.nextInt(21)); } } catch (NoSuchAlgorithmException nsae) { // Forward to handler } }
Exceptions
MSC02-J-EX0: Using the default constructor for java.util.Random
applies a seed value that is "very likely to be distinct from any other invocation of this constructor" [API 2014] and may improve security marginally. As a result, it may be used only for noncritical applications operating on nonsensitive data. Java's default seed uses the system's time in milliseconds. When used, explicit documentation of this exception is required.
import java.util.Random; // ... Random number = new Random(); // Used only for demo purposes int n; //... for (int i = 0; i < 20; i++) { // Reseed generator number = new Random(); // Generate another random integer in the range [0, 20] n = number.nextInt(21); System.out.println(n); }
For noncritical cases, such as adding some randomness to a game or unit testing, the use of class Random
is acceptable. However, it is worth reiterating that the resulting low-entropy random numbers are insufficiently random to be used for more security-critical applications, such as cryptography.
MSC02-J-EX1: Predictable sequences of pseudorandom numbers are required in some cases, such as when running regression tests of program behavior. Use of the insecure java.util.Random
class is permitted in such cases. However, security-related applications may invoke this exception only for testing purposes; this exception may not be applied in a production context.
Risk Assessment
Predictable random number sequences can weaken the security of critical applications such as cryptography.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MSC02-J | High | Probable | Medium | P12 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
CodeSonar | 8.1p0 | JAVA.HARDCODED.SEED | Hardcoded Random Seed (Java) |
Coverity | 7.5 | RISKY_CRYPTO | Implemented |
Parasoft Jtest | 2024.1 | CRT.MSC02.SRD | Use 'java.security.SecureRandom' instead of 'java.util.Random' or 'Math.random()' |
SonarQube | 9.9 | S2245 |
Related Vulnerabilities
CVE-2006-6969 describes a vulnerability that enables attackers to guess session identifiers, bypass authentication requirements, and conduct cross-site request forgery attacks.
Related Guidelines
MSC30-C. Do not use the rand() function for generating pseudorandom numbers | |
MSC50-CPP. Do not use std::rand() for generating pseudorandom numbers | |
CWE-327, Use of a Broken or Risky Cryptographic Algorithm CWE-330, Use of Insufficiently Random Values CWE-332, Insufficient Entropy in PRNG CWE-336, Same Seed in PRNG CWE-337, Predictable Seed in PRNG |
13 Comments
David Svoboda
At least, it needs to catch NoSuchAlgorithmException or declare it throws this exception.
Georgios Katsis
As far as the 2nd noncompliant example is concerned, I think you are right. I was trying to show that by continuously "resetting" the generator's instance you do not let the algorithm to work out. By the randomness perspective though, due to the use of CPU's time, it could be a good tactic to get better "real" random numbers, provided that an attacker cannot use/change system's time.
David Svoboda
Dhruv Mohindra
I feel the first CS might be better as an exception. I always fear that someone might copy paste the CS code without reading the text properly.
David Svoboda
Our main criteria for making an exception to a rule is that the exception must be automatically. enforceable. IOW a sufficient rule checker should be able to tell when some code falls into the exception category. This can prob be done with annotations, or Javadoc comments.
So the first CS can be moved into an exception if you think that is best.
BTW the first CS would not fly in C or C++ because they don't specify how rand() works. Consequently, rand() need not be truly random (and there have been poor implementations in the past that cause security experts to not trust rand()). So seeding the standard RNG is only a compliant solution in Java.
Dhruv Mohindra
I've made the change. Thanks for the c/c++ ideas; I believe methods of class
Random
in Java were not meant for secure use even if they could be seeded (as described in the docs). So c/c++ & Java are alike in that respect.Incidentally, subclasses of
Random
are permitted to use their own algorithm provided they follow the general contract.Dhruv Mohindra
Don't know how feasible it is to recommend generating strong random numbers outright. If you generate random numbers per client on a linux machine, the calls may block if the pool is exhausted (depends on /dev/random for entropy). Might be worth allowing precalculated seed data to be supplied from other pools/systems.
A Bishop
SecureRandom on some operating systems can get very slow if their entropy source is depleted. I've seen issues on RHEL 6 where our unit tests were taking 82 minutes compared to 5 minutes on Windows & other linux variants.
David Svoboda
Why is the Seveirty High? It strikes me that violation of this rule might compromise an encryption system based on an insufficiently random number. But that results in an information leak, not remote code execution. So the security should be Medium.
Fred Long
Being able to predict a random number sequence may enable authentication to be bypassed, which could result in a system being compromised completely, including remote code injection. The "Related Vulnerabilities" section refers to just such an attack. Therefore, I think a severity of "High" is justified.
David Svoboda
OK. The analogous C rule is 'Medium', because we didn't consider privilege escalation when building the C standard.
Maarten Bodewes
new SecureRandom() should be preferred over explicitly defining an algorithm as this rule does. Also verify this with the similar recommendation that does use new SecureRandom.
The name of this rule should be revised or it should be mentioned explicitly that this isn't about getInstanceStrong as introduced in Java 8.
As for depletion of the entropy pool, see http://openjdk.java.net/jeps/123 which is integrated into Java 8.
David Svoboda
I've updated the rule as suggested. There is a new compliant example that uses the getInstanceStrong() method.