Never use Math.random() to create passwords in JavaScript
Nico Zerpa (he/him)

Nico Zerpa (he/him) @nicozerpa

About: Your JavaScript friend! I've been working for over a decade in JS, and I want to help you level up your skills

Location:
Buenos Aires, Argentina
Joined:
Mar 7, 2021

Never use Math.random() to create passwords in JavaScript

Publish Date: Nov 25 '21
5 3

Recently, I've seen articles and tweets that show how to build random password generators with JavaScript. These projects are excellent to get some practice, sure. However, the passwords they create are not secure.

These password generators rely on the Math.random() method, which is not cryptographically secure. It means the pseudo-random number is not really that random, that the random numbers can be predicted and, therefore, it is possible to guess the generated passwords.

It's worth pointing out that JavaScript wasn't originally created to build "serious" applications. Its original purpose was to add just some interactivity and visual effects to web pages.

For that reason, when Math.random() was designed, nobody thought about making it cryptographically secure. it wasn't seen as necessary back then.

Nowadays, the language has evolved and you can now build complex projects with it, but it still has many traces of its past, and these must be kept for compatibility reasons.

How to create secure passwords

On the front end, you can use the crypto.getRandomValues() method to create random numbers that are secure enough. If you're using Node.js, the crypto module has the randomInt() method.

Here's a password generator for the front end, using crypto.getRandomValues():

function generatePassword(length = 16)
{
    let generatedPassword = "";

    const validChars = "0123456789" +
        "abcdefghijklmnopqrstuvwxyz" +
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        ",.-{}+!\"#$%/()=?";

    for (let i = 0; i < length; i++) {
        let randomNumber = crypto.getRandomValues(new Uint32Array(1))[0];
        randomNumber = randomNumber / 0x100000000;
        randomNumber = Math.floor(randomNumber * validChars.length);

        generatedPassword += validChars[randomNumber];
    }

    return generatedPassword;
}
Enter fullscreen mode Exit fullscreen mode

And this is another generator for Node.js:

const util = require("util");
const crypto = require("crypto");

const randomInt = util.promisify(crypto.randomInt);

async function generatePassword(length = 16)
{
    let generatedPassword = "";

    const validChars = "0123456789" +
        "abcdefghijklmnopqrstuvwxyz" +
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        ",.-{}+!\"#$%/()=?";

    for (let i = 0; i < length; i++) {
        generatedPassword += validChars[await randomInt(0, validChars.length)];
    }

    return generatedPassword;
}
Enter fullscreen mode Exit fullscreen mode

Become a Better JavaScript Developer! My newsletter has easy, actionable steps to level up your JavaScript skills, right to your inbox. Click here to subscribe

Comments 3 total

  • Alex Lohr
    Alex LohrNov 25, 2021

    Before we got the crypto module, we collected mouse and keyboard events to increase the entropy of the random values. It's a lot better now.

    • Shrihan
      ShrihanNov 26, 2021

      On an unrelated note, some tools like gpg do that even now; they tell users to spawn processes or conduct R/W operations to generate a secure key.

  • Stas Klymenko
    Stas KlymenkoNov 26, 2021

    How exactly you will get the password generated with Math.random()?

Add comment