Alexander Nnakwue Software engineer. React, Node.js, Python, and other developer tools and libraries.

Building a random number generator with JavaScript and Node.js

6 min read 1692

Introduction

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.

Use cases for the Math.random() method in JavaScript

Math.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 -&gt; //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:

We made a custom demo for .
No really. Click here to check it out.

const max = 6
const randomNumber = Math.floor(Math.random() * max)
console.log(randomNumber) outputs -&gt; 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.

Security pitfalls in 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:

  • Inadequate, and usually biased, logic employed in generating random integers within a uniform distribution
  • Browser inconsistencies as to how many bits/bytes of randomness to utilize
  • Random results are always difficult to replay consistently, making it non-deterministic and irregular in nature
  • The built-in seed can be tampered with, making it unsuitable in terms of its integrity

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.

Introduction to the Web Crypto API

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.

Using the Web Crypto API

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.

Syntax

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);

Parameters

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.

Return value

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.

Generating 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:

  • For generating random numbers in a range, there is random-number-csprng which uses the Crypto API under the hood
  • For API keys or tokens, there is uuid, and specifically the uuid.v4() function

Let’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: 
[email protected] random-number-generator % node index.js 
Your random number: 24
[email protected] random-number-generator % node index.js
Your random number: 14
[email protected] random-number-generator % node index.js
Your random number: 20
[email protected] random-number-generator % node index.js
Your random number: 21
[email protected] random-number-generator % node index.js
Your random number: 11
[email protected] random-number-generator % node index.js
Your random number: 26
[email protected] random-number-generator % node index.js
Your random number: 23
[email protected] random-number-generator % node index.js
Your random number: 15
[email protected] random-number-generator % node index.js
Your random number: 22
[email protected] 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.

Using the Web Crypto API in Node.js

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.

Conclusion

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.

200’s only Monitor failed and slow network requests in production

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. https://logrocket.com/signup/

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. .
Alexander Nnakwue Software engineer. React, Node.js, Python, and other developer tools and libraries.

Leave a Reply