There are a several use cases in which a program might require a secure source of random number generation. Typically, we need random numbers for game functions like dice or raffles, private key generation, or other similar programs that require a cryptographically secure source.
In JavaScript, we have the native implementation of the random()
method in the Math
object, a built-in object that has properties or methods for performing mathematical calculations. The random()
method helps us generate random numbers, as the name implies.
The Math.random()
method returns a decimal number or floating-point, pseudo-random number between zero (inclusive) and one (exclusive). In mathematical terms, this is represented as 0 <= x < 1
.
While there are different ways of using this method to yield random results over certain ranges, Math.random()
is not a true random number generator. This is because it is pseudo-random; over time, the numbers will begin to repeat and eventually display a non-random pattern. As you may know, it is difficult for computers to generate truly random numbers.
However, while the pseudo-random numbers generated by Math.random()
are usually sufficient in most cases, sometimes we need a cryptographically secure source of random number generation. What I mean is that we want random numbers that cannot easily be guessed via a pattern, or that end up repeating over time.
In this article, we are going to focus on the recommended approach for these cases. But before that, let us examine some of the basic use cases of the Math.random()
method, so we can learn the simplest way of generating random numbers in JavaScript.
Math.random()
method in JavaScriptMath.random()
returns a non-cryptographic random number, which starts from a hidden internal representation called a “seed”. The seed represents the starting point for a hidden sequence of numbers uniformly generated over a specified range. Some of the use cases for this method are explained below.
The first (and simplest) use case for the Math.random()
method is generating random decimal or floating point numbers between zero and one:
const randomNumber = Math.random() console.log(randomNumber) outputs -> //0.8446144685704831
Note that by generating random numbers between zero and one, we can scale the result up to whatever size we need by multiplying the random number with another number.
For example:
const max = 6 const randomNumber = Math.floor(Math.random() * max) console.log(randomNumber) outputs -> 0,1,2,3,4,5
Another use case for this method is generating a random integer between two specific integer ranges with another method in the Maths
object called floor
. The floor
method returns the largest integer less than or equal to the specified number.
We can generate a floating point number within a specified range, like in the example below:
// generating floating point numbers within a range const max = 4 const min= 2 const result = Math.random()*(max - min) console.log(result) //1.4597999233994834 // generating random integers within a range const max = 4 const min= 2 const result = Math.random()*(max - min) + min console.log(Math.floor(result))//3
Other use cases for the Math.random()
function include generating random numbers between a certain specified range with a maximum limit. Here, we can make use of ceil
, another method in the Maths
object.
Now, in the next section, let’s explore a few of the downsides of utilizing these pseudo-random number generators.
Math.random()
Math.random()
comes with a couple of downsides in terms of security. According to the MDN documentation, Math.random()
does not guarantee cryptographically secure random numbers. Therefore, it is advisable not to use them for anything related to security in our program.
These security faults are due partly to the following:
As a result of these loopholes, The World Wide Web Consortium came up with an implementation of the Web Crypto API. At the time of writing, all popular browsers provide an implementation of this API to JavaScript applications via the crypto
object.
Let’s quickly look at the Web Crypto API and how to use it.
The Web Crypto API provides a number of cryptographic methods and functions that can be accessed through the Window.crypto
property. In browsers, we can utilize the crypto.getRandomValues(Int32Array)
method, which promises cryptographically random number generation.
On the server side, Node.js also provides an implementation of the standard Web Crypto API. To make use of this module, we can initialize it with require('crypto').randomBytes(size)
, because the crypto package is native to Node.
The pseudo-random number generator algorithm (PRNG) used in the Web Crypto API may vary across different browser clients. However, it is suitable for most cryptographic purposes, insofar as the internal seeds have enough entropy, possibly from an external source, like Unix /dev/urandom
.
In the next section, we will talk about how to make use of the Web Crypto API, including the syntax, the parameters we can pass, and the return values. Let’s begin with the basic usage below.
The Crypto.getRandomValues()
method lets us get cryptographically strong random values.
In essence, the Crypto
interface represents a general purpose cryptographic functionality.
It is available in most web browsers, and although the implementations may vary, they are all required to use a seed with enough entropy. This is to ensure there is no negative impact on performance and security.
The getRandomValues()
method is the only member of the Crypto
interface that can be used from an insecure context. Therefore, it is not advisable to use when generating encryption keys, because they are not guaranteed to return safe results. In this case, we can make use of the generateKey()
method.
Now, let us look at the syntax, accepted parameters, and return value of the getRandomValues
method of the web crypto API.
The Web Cryptography API accepts instances of the ArrayBuffer
class and TypedArray
as an input to represent byte sequences.
See the syntax below:
typedArray = cryptoObj.getRandomValues(typedArray);
typedArray
is an integer-based TypedArray
object. It can be either an Int8Array
, a Uint8Array
, an Int16Array
, a Uint16Array
, an Int32Array
, or a Uint32Array
.
Note that all elements in the array are filled with random numbers.
The return value is the same array passed in as a typedArray
, but with its contents replaced with the newly generated random number.
In the next section, we are going to look at a how to write a simple program that generates secure cryptographically random numbers.
Every random value that you need for security purposes (i.e., anywhere there exists the possibility of an attacker), should be generated using a Cryptographically Secure Pseudo-Random Number Generator, also known as a CSPRNG.
This includes verification or reset tokens, lottery numbers, API keys, generated passwords, encryption keys, and so on.
So, how can we generate safe and secure random numbers? The best option is to adopt a library that takes care of these security issues by design. In Node, we have a few options:
uuid.v4()
functionLet’s see an example below.
Spin up a simple Node application to adopt the easiest example from the npm package:
var Promise = require("bluebird"); var randomNumber = require("random-number-csprng"); Promise.try(function() { return randomNumber(10, 30); }).then(function(number) { console.log("Your random number:", number); }).catch({code: "RandomGenerationError"}, function(err) { console.log("Something went wrong!"); }); Output shown below: retina@alexander random-number-generator % node index.js Your random number: 24 retina@alexander random-number-generator % node index.js Your random number: 14 retina@alexander random-number-generator % node index.js Your random number: 20 retina@alexander random-number-generator % node index.js Your random number: 21 retina@alexander random-number-generator % node index.js Your random number: 11 retina@alexander random-number-generator % node index.js Your random number: 26 retina@alexander random-number-generator % node index.js Your random number: 23 retina@alexander random-number-generator % node index.js Your random number: 15 retina@alexander random-number-generator % node index.js Your random number: 22 retina@alexander random-number-generator % node index.js Your random number: 28
As seen in the example above, we can generate cryptographically secure pseudo-random numbers within a range. The randomNumber
method returns a promise that resolves to a random number within the specified range. More details can be found on the GitHub repository API section.
Node.js provides an implementation of the standard Web Crypto API, although at the time of writing, it is still an experimental feature of the language.
We can make use of this module in Node by calling require('crypto').webcrypto
. This returns an instance of the Crypto
class, which provides access to the remainder of the crypto API.
As we have earlier discussed,
crypto.getRandomValues(typedArray)
generates cryptographically strong random values. More details on the web crypto API in Node can be found on the Node documentation.
It’s debatable whether true randomness, to which no pattern or algorithm applies, really exists. For security-related code however, we need a random number impossible for an attacker to predict. In this case, it doesn’t matter how the data is generated, as long as it can’t be guessed.
To this end, the World Wide Web Consortium published the Web Cryptography API, which allows JavaScript applications in browsers to use common cryptographic features without having to use any third-party libraries.
And as we have mentioned earlier, the crypto.getRandomValues
method of this API is the most secure way for web applications to obtain cryptographically secure random data.
All other features of the Web Crypto API are accessible through the crypto.subtle
object. A link to the web cryptographic standard can be found here.
Thanks for reading 🙂 and please don’t hesitate to drop your questions or comments in the section below.
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]