Secure random values (in Node.js)
Not all random values are created equal - for security-related code, you need a specific kind of random value.
A summary of this article, if you don't want to read the entire thing:
Math.random()
. There are extremely few cases where Math.random()
is the right answer. Don't use it, unless you've read this entire article, and determined that it's necessary for your case.crypto.getRandomBytes
directly. While it's a CSPRNG, it's easy to bias the result when 'transforming' it, such that the output becomes more predictable.uuid
, specifically the uuid.v4()
method. Avoid node-uuid
- it's not the same package, and doesn't produce reliably secure random values.random-number-csprng
.You should seriously consider reading the entire article, though - it's not that long :)
There exist roughly three types of "random":
Irregular data is fast to generate, but utterly worthless for security purposes - even if it doesn't seem like there's a pattern, there is almost always a way for an attacker to predict what the values are going to be. The only realistic usecase for irregular data is things that are represented visually, such as game elements or randomly generated phrases on a joke site.
Unpredictable data is a bit slower to generate, but still fast enough for most cases, and it's sufficiently hard to guess that it will be attacker-resistant. Unpredictable data is provided by what's called a CSPRNG.
Every random value that you need for security-related purposes (ie. anything where there exists the possibility of an "attacker"), should be generated using a CSPRNG. This includes verification tokens, reset tokens, lottery numbers, API keys, generated passwords, encryption keys, and so on, and so on.
In Node.js, the most widely available CSPRNG is the crypto.randomBytes
function, but you shouldn't use this directly, as it's easy to mess up and "bias" your random values - that is, making it more likely that a specific value or set of values is picked.
A common example of this mistake is using the %
modulo operator when you have less than 256 possibilities (since a single byte has 256 possible values). Doing so actually makes lower values more likely to be picked than higher values.
For example, let's say that you have 36 possible random values - 0-9
plus every lowercase letter in a-z
. A naive implementation might look something like this:
let randomCharacter = randomByte % 36;
That code is broken and insecure. With the code above, you essentially create the following ranges (all inclusive):
If you look at the above list of ranges you'll notice that while there are 7 possible values for each randomCharacter
between 4 and 35 (inclusive), there are 8 possible values for each randomCharacter
between 0 and 3 (inclusive). This means that while there's a 2.64% chance of getting a value between 4 and 35 (inclusive), there's a 3.02% chance of getting a value between 0 and 3 (inclusive).
This kind of difference may look small, but it's an easy and effective way for an attacker to reduce the amount of guesses they need when bruteforcing something. And this is only one way in which you can make your random values insecure, despite them originally coming from a secure random source.
In Node.js:
random-number-csprng
.uuid
(not node-uuid
!), specifically the uuid.v4()
method.Both of these use a CSPRNG, and 'transform' the bytes in an unbiased (ie. secure) way.