DEV Community

Nico Zerpa (he/him)
Nico Zerpa (he/him)

Posted on • Originally published at nicozerpa.com

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

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

Top comments (3)

Collapse
 
lexlohr profile image
Alex Lohr

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.

Collapse
 
shrihankp profile image
Shrihan

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.

Collapse
 
hellnar profile image
Stas Klymenko

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