Skip to content
ansezz.

▸ Free tool

Secret & API Key Generator.

Cryptographically secure secrets, API keys, and passphrases — drawn from crypto.getRandomValues, never Math.random. Nothing is transmitted, nothing is logged.

▸ Local only

Generated in this tab with the browser CSPRNG. No request is made, no value is stored. Reload the page and they are gone for good.

Preset

8 128
Character sets

0 bits

Strength ·

Alphabet:

The randomness comes from the OS, not from JavaScript

crypto.getRandomValues() is not a JavaScript random number generator. It is a thin wrapper over the platform CSPRNG — getrandom(2) on Linux, BCryptGenRandom on Windows, SecRandomCopyBytes on macOS — the same source your server reaches for when it runs openssl rand. It is seeded from hardware entropy, it is continuously reseeded by the kernel, and its output does not leak its internal state.

Math.random() fails all three of those. V8 implements it as xorshift128+, seeded once when the context is created and never reseeded. Its 128-bit state is recoverable from a modest run of consecutive outputs, and once you have the state you can compute every value it will ever return — forwards and backwards. That is fine for a confetti animation and a full compromise for a session token. This page never calls it. Not for the strings, not for the passphrases, not for anything.

  Math.random() crypto.getRandomValues()
Source xorshift128+ in V8 OS CSPRNG
State recoverable from output Yes — a short run is enough No
Reseeded Once, at context creation Continuously, by the kernel
Uniform after % n No No — still needs rejection sampling
Safe for secrets

Why plain byte % length is a bug

A Uint8Array gives you values 0–255, and your alphabet is almost never a divisor of 256. Take 62 alphanumeric characters: 256 = 4 × 62 + 8. Bytes 0–7 land on the same index as bytes 62–69, 124–131, 186–193 and 248–255 — five routes each — while every other index has only four. The first eight characters of your alphabet show up roughly 25% more often than the rest.

That is not a catastrophic break, but it is a free ordering hint for anyone building a guess list, and it quietly shaves entropy off every secret you issue. The fix is rejection sampling and it costs nothing but a few discarded bytes — about 3% of draws for a 62-character alphabet, 30% for the full 89-character one. Compute limit = Math.floor(256 / size) * size, throw away any byte at or above limit, and take the modulo of what survives. Every index then has exactly equal probability. That is what runs when you click Generate above. Hex and base64url skip the rejection entirely, because 16 and 64 both divide 256.

What 128 bits actually buys you

Entropy is length × log₂(alphabetSize), and the readout above shows the exact figure for whatever you have configured. 128 bits means 3.4 × 10³⁸ candidate values. Give an attacker a billion machines each testing a trillion candidates per second — 10²¹ guesses a second, which nobody on earth has — and the expected time to land on yours is still around five billion years.

So 128 bits ends the conversation. 32 hex characters is exactly 128 bits. 22 alphanumeric characters is 131. A v4 UUID carries 122 — close enough to the bar that people reach for one as an API key. Don't. UUIDs end up in URLs, access logs, and error messages precisely because they are built to be identifiers, not secrets. Going past 128 bits is not more secure either, it is a longer line in your .env, and it does nothing about the way secrets are actually compromised, which is leaking rather than guessing. The symbol set here also deliberately omits quotes, backticks, backslashes and spaces: they buy well under a bit per character and cost you an afternoon the first time a secret containing a backtick passes through a shell script, a dotenv parser and a YAML file.

Generating is the easy part

The value above is worth nothing the moment it lands somewhere durable and unencrypted. It belongs in a secret manager — Vault, AWS Secrets Manager, Doppler, 1Password, or your platform's encrypted environment store if you self-host — injected at boot, never committed. If you have ever run git add -A in a directory containing a real .env, go check your history before you read further.

Rotation is a design decision, not a chore. Your verifier has to accept the old value and the new one during an overlap window; if it cannot, "rotating" is a synonym for "brief outage", which is exactly why so many teams never rotate anything. Give every environment its own value — a staging key that also opens production is one leaked CI log away from being a production breach. Rotate on a schedule, on offboarding, and immediately on suspicion. "I think it's fine" is not an incident response.

Questions, answered.

Is it safe to generate secrets in a browser?

Yes, as long as the generator uses the platform CSPRNG and never sends the value anywhere. This page calls crypto.getRandomValues, which is backed by the same operating-system entropy source your server reaches for, and the generated values never leave the tab — no network request, no analytics event, no logging. Open devtools and watch the network panel while you click Generate. For a root signing key on a hardened host, generating on that host with openssl rand is still the tighter move.

Why is Math.random() unsafe for passwords and API keys?

Math.random() is a fast non-cryptographic PRNG — V8 implements it as xorshift128+ — seeded once when the context is created and never reseeded. Its internal state can be recovered from a modest run of outputs, and once an attacker has the state they can compute every value it will ever return, forwards and backwards. That is fine for shuffling a carousel and catastrophic for a session token, a password-reset link, or an API key.

How many bits of entropy does an API key need?

128 bits is the standard bar and there is very little reason to exceed it. That is 32 hex characters, 22 alphanumeric characters, or 16 raw bytes. Below roughly 64 bits you are inside reach of a determined offline attacker with commodity GPUs; above 128 you are only making your .env line longer, because the realistic compromise is the key leaking, not the key being guessed.

What is modulo bias, and does this generator avoid it?

Modulo bias is what you get when you map a random byte onto an alphabet that does not divide 256 evenly. With a 62-character alphabet, 256 = 4 × 62 + 8, so the first eight characters come up five times per 256 bytes while the rest come up four — about 25% more often. This tool computes limit = Math.floor(256 / alphabetSize) * alphabetSize, discards every byte at or above that limit, and only then takes the modulo, so each character is exactly equally likely.

Are the passphrases here as strong as Diceware?

No, and I would rather say so on the page than let you assume otherwise. The wordlist embedded here is 128 words, which is exactly 7 bits per word, so an eight-word passphrase is 56 bits. The EFF Diceware list is 7,776 words — about 12.9 bits per word — so six Diceware words beat eleven words from this list. Use passphrase mode for things a human has to type and remember, and random-string mode for anything a machine reads.

Where should I store the secret after generating it?

In a secret manager — Vault, AWS Secrets Manager, Doppler, 1Password, or your platform's encrypted environment store — and nowhere else. Not in the repository, not in a Slack message, not in a ticket. If a secret has ever touched shell history, a screen share, or a chat log, treat it as burned and rotate it. Rotation only works if your verifier accepts the old and the new value during an overlap window, so build that before you need it.

Keep reading

▸ Everything on this page runs client-side. No secret is transmitted, stored, or logged.