Blog

How to generate a secure random password (best practices)

How password strength is measured, why Math.random() is the wrong tool, how to generate passwords with the Web Crypto API, and the habits that make passwords easy to crack.

February 10, 2026

A site asks for a password with at least one capital, one number and one symbol. You type Summer2026!, it turns green, and you move on. The form is happy. An attacker’s cracking tool is happy too, because that’s one of the first things it tries.

Passkeys and two-factor logins are catching on, but most accounts still hang on a password. This guide covers what actually makes one strong, why some popular ways of generating them are quietly broken, and how to do it properly.

Password entropy explained

Entropy is a measure of how hard a password is to guess, counted in bits. If every character is picked at random, it’s L × log2(N), where N is how many characters you’re choosing from and L is the length.

LengthCharacter setSet sizeEntropy
8Lowercase2637.6 bits
8Letters and digits6247.6 bits
12Printable ASCII9578.8 bits
16Printable ASCII95105.1 bits

“Printable ASCII” means every character on a US keyboard: letters, digits, symbols and the space. Each extra bit doubles the number of guesses an attacker needs. 40 bits is about a trillion possibilities. 80 bits is about 1.2 × 1024. 128 bits is about 3.4 × 1038, far beyond what anyone can search.

There’s a catch: the formula assumes the characters really were picked at random. A password you invent has much less entropy than its length suggests, because humans love patterns and attackers know which ones.

How much entropy is enough

It depends on how the attacker gets to guess. In an online attack, they type guesses into a login form, and rate limits or lockouts stop them after a handful of tries. Even 40 bits holds up fine there.

In an offline attack, they’ve stolen a database of password hashes (the scrambled form sites store instead of the password) and can guess as fast as their hardware allows. One modern GPU computes tens of billions of MD5 or SHA-1 hashes per second, enough to work through 50 bits in a few hours. Slow password hashes such as bcrypt and Argon2 are designed to make every guess expensive, and turn those hours into years.

You can’t know how a site stores your password, so plan for the offline case. A common rule of thumb is at least 80 bits for everyday accounts and 128 for anything critical. A random 16-character password from the full printable set clears the first bar comfortably.

Why Math.random() is insecure

Plenty of password generators, online and in real codebases, use JavaScript’s Math.random(). It’s a pseudorandom number generator (PRNG): an algorithm that produces numbers that look random. It was built for speed and even spread in games and simulations. Security was never the job.

Its output can be predicted

V8, the engine inside Chrome and Node.js, implements Math.random() with an algorithm called xorshift128+. Its entire internal state is 128 bits, and that state can be worked out from a small number of outputs. Once someone has it, they can calculate every value the generator produced before and after, including the characters of your password.

It makes no security promises

A cryptographically secure PRNG (CSPRNG) guarantees that seeing any number of its outputs doesn’t help predict the next one. The JavaScript spec makes no such promise for Math.random(), and each engine can use whatever algorithm it likes.

The secure alternative: crypto.getRandomValues()

The Web Crypto API has crypto.getRandomValues(), which fills an array with numbers from a CSPRNG that the operating system seeds from hardware noise. Anything security-related in the browser should use it.

Insecure: Math.random()
// Do not use this for passwords
function insecurePassword(length) {
  const chars =
    "abcdefghijklmnopqrstuvwxyz" +
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
    "0123456789!@#$%^&*";
  let password = "";
  for (let i = 0; i < length; i++) {
    password += chars[Math.floor(Math.random() * chars.length)];
  }
  return password;
}
Secure: crypto.getRandomValues()
function securePassword(length) {
  const chars =
    "abcdefghijklmnopqrstuvwxyz" +
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
    "0123456789!@#$%^&*";
  const randomValues = new Uint32Array(length);
  crypto.getRandomValues(randomValues);
  let password = "";
  for (let i = 0; i < length; i++) {
    password += chars[randomValues[i] % chars.length];
  }
  return password;
}

Spot the difference? It’s a couple of lines. But the second version stays unpredictable even to someone who has read the code and seen earlier passwords.

A small footnote for the careful reader: the % step makes a few characters very slightly more likely than others, because 232 doesn’t divide evenly by the set size. With 32-bit values and fewer than 100 characters, the effect is too small to matter.

In Node.js, reach for crypto.randomInt(max) from the built-in node:crypto module, which returns an unbiased integer, or crypto.randomBytes(). Current Node versions also have crypto.getRandomValues() available globally.

How the character set affects strength

Character setSizeBits per character16 characters
Digits (0-9)103.3253 bits
Lowercase (a-z)264.7075 bits
Lowercase and uppercase525.7091 bits
Letters and digits625.9595 bits
Printable ASCII956.57105 bits

Symbols help, but they’re a pain on a phone keyboard, and some sites reject certain ones for reasons nobody can explain. Length matters more than variety. A 20-character password of just letters and digits has about 119 bits and never trips over a banned symbol.

Passphrases

A passphrase is a handful of randomly chosen words, like the famous xkcd example “correct horse battery staple”. How strong it is depends on the size of the word list and how many words you use.

The EFF’s dice word list has 7,776 words, so each word adds log2(7776) ≈ 12.9 bits. Six words give about 77 bits and eight give about 103. Both are much easier to remember than xK9#mP2$vL7@nQ4.

The words have to be picked at random, with real dice or a generator. Words you choose yourself drift toward the common and the personal (your dog’s name, your football team), and the real entropy drops sharply.

Password manager tips

Nobody can remember 200 unique random passwords. A password manager doesn’t have to. It generates, stores and fills them for you, which solves most of the problem in one go.

  • Use a unique password everywhere. Then a breach at one site exposes only that one account.
  • Generate at least 16 characters. The manager does the typing, so there’s no reason to go shorter.
  • Pick a strong master password. A random passphrase of six or more words is long and still memorable. It’s the one password you have to keep in your head.
  • Turn on two-factor authentication for the manager. A hardware security key or an authenticator app beats SMS.
  • Keep an encrypted backup. Export the vault now and then and store it somewhere separate, so losing a device doesn’t mean losing everything.
  • Run the security audit. Most managers flag weak, reused and breached passwords. Check the report every few months.

Common password mistakes

Dictionary words

A single word is weak, even dressed up as p@ssw0rd. Cracking tools try every word in many languages along with all the usual letter swaps, and p@ssw0rd is in every list they use.

Personal information

Names, birthdays, pets, street names and phone numbers are all a quick scroll through your social media away. Anyone targeting you will try them first.

Keyboard patterns

qwerty, 123456 and asdfghjkl are some of the most common passwords in the world. Cracking tools try keyboard walks almost immediately.

Simple transformations

Adding 123 or ! to the end, capitalising the first letter, swapping in the current year: cracking tools apply all of these rules automatically. Password1! satisfies most complexity checks and is still one of the first guesses.

Reusing passwords

This is the most dangerous habit of the lot. After a breach, attackers take the leaked email and password pairs and try them on other popular sites. That’s called credential stuffing. If your email password matches the one you used on that forum that got hacked in 2019, your email is exposed too.

Short passwords

Under 12 characters, even a fully random password is within reach of an offline attack when the site uses a fast hash. Every possible 8-character printable ASCII password can be tried in under a day on a single high-end GPU with a tool such as Hashcat.

Password strength checklist

  • At least 16 characters, and 20 or more where you can.
  • Generated by a cryptographically secure random generator, not made up by a person.
  • A mix of uppercase, lowercase, digits and, where allowed, symbols.
  • Used for this one account only.
  • Stored in a password manager, not on a sticky note or in passwords.txt on your desktop.
  • Backed by two-factor authentication wherever it’s offered.

Using the PulpMiner password generator

The free password generator uses crypto.getRandomValues() and runs entirely in your browser, so the passwords never leave your device.

  • Set the length anywhere from 8 to 128 characters. The default is 16.
  • Turn uppercase, lowercase, numbers and symbols on or off. With all four on, it picks from 92 characters.
  • Turn on Exclude ambiguous to drop 0 O o I l 1, the characters that look alike when you have to read a password off one screen and type it into another.
  • Generate up to 10 passwords at once and copy each one.
  • A strength meter shows the estimated entropy in bits and how many characters it’s choosing from.

Need data from a webpage as JSON?

PulpMiner turns any public page into structured JSON and can save it as a live API. See it work on a demo page, no sign-up.

Try it on a demo page