Software Integrity

 

Pseudorandom number generation means pseudosecurity

In 2014 an exploit was discovered in Firefox for Android that allowed malicious applications access to sensitive user data. The cause? An unfortunately predictable PRNG called Math.random().

If you’re using NodeJS (or any other JavaScript environment) in your stack today, the same Math.random() function is your default PRNG.

Don’t worry, though. We’ve got your back.

Here’s a breakdown of exactly where Math.random() goes astray, and some possible PRNG alternatives you can use to make your projects more secure.

Putting the pseudo in pseudorandom

There’s really nothing random about Math.random(). By definition, PRNGs generate numbers deterministically based on a set of initial seed value: they can only approximate randomness. While there are PRNGs that come a stone’s throw from true randomness (we’ll get to those), Math.random() is widely regarded as sub-par.

What does this mean?

There is a chance that the “randomly” generated values produced by JavaScript’s baked-in PRNG will be repeated (or, under the wrong circumstances, could be predicted by an adversary). For scenarios that involve random selection—a simple dice game, or for internal QA testing where there’s less emphasis on unique session ID values and cryptography—this can be frustrating.

But in the event you’re currently using Math.random() where unique values are critical—to create unique session IDs, generate values across active users, or for encryption—there could be major security implications.

All PRNGs are not created equal

Luckily, for these security-centered scenarios you have the option of Cryptographically Secure PseudoRandom Number Generators (CSPRNGs). These are designed to resist a would-be attacker’s ability to identify the seed value and predict subsequent outputs in a sequence.

Since security isn’t the only place where random number generation is necessary, it’s important to understand the non-cryptographic alternatives to Math.Random(). And because JavaScript’s default PRNG is found wanting in virtually every measurable category—from predictability to statistical quality to speed—we absolutely recommend upgrading.

With that in mind, we’ve put together this list of more powerful, easy-to-implement PRNGs for you to use in your application:

Xorshift

Xorshift random number generators create sequences by “repeatedly taking the exclusive of a number with a bit shifter version of itself.” It passes more statistical tests than Math.random() and, because of the way it structures sequences, it’s much faster.

PCG

According to the (in no way biased) chart below, PCG random number generators are the cream of the crop. They’re difficult to predict, extremely fast, compact, and statistically excellent.

pseudorandom number generation

Mersenne Twister

The Mersenne Twister is the most widely-used PRNG; it’s the default in Python, R, Ruby, and a slew of other languages. Why JavaScript isn’t one of them is a mystery. Downsides? It’s not very fast (especially when compared to Xorshift and PCG) and takes up a ton of space.

CSPRNG

While the random number generators listed above are invariably better than Math.random(), their lack of a cryptographic component ultimately renders them inferior to CSPRNGs when used for security.

By default, the NodeJS library contains crypto.randombytes(), a CSPRNG that uses entropy from the underlying operating system to create near-true randomness. For example, on Linux crypto.randomBytes()takes the entropy from /dev/urandom, and generates random bytes which can be used to create secure pseudorandom numbers.

Stacked up against Math.random(), crypto.randomBytes() looks even more impressive: it generates a table of unique possible random values on each call compared to a static table generated only once byMath.random().

Math.random(): Not completely useless

While Math.random() isn’t as powerful a PRNG as Xorshift, PCG, or the Mersenne Twister, it does still generate pseudorandom numbers. If you aren’t using your PRNG to create and track sessions or for some other application in which an increased chance of recurring values could be cataclysmic, Math.random()could make sense.

That being said, any of the other three PRNGs would be better: they’re faster, more powerful, more efficient. And, in a security context, not even these options will cut it: the cryptographic alternative is a necessity.