DevKitHub

API & Security

Bcrypt Hash Generator & Checker

Hash a password with bcrypt, or check one against an existing $2a$, $2b$ or $2y$ hash. Both run in a background thread in your browser, so the password never leaves your machine.

Generate

28 / 72 bytes

Cost 10 is 1,024 rounds. Each step up doubles the time; OWASP recommends 10 or more.

Verify

This tool runs entirely in your browser. Your input is never uploaded, stored or logged.

How it works

bcrypt is the Blowfish cipher with a deliberately ruinous key schedule. Blowfish normally sets itself up once per key; bcrypt, as Provos and Mazières designed it in 1999, starts from Blowfish’s tables — the hexadecimal digits of pi — mixes in the salt and the password, then repeats that expensive setup 2^cost times, alternating password and salt, before encrypting the text “OrpheanBeholderScryDoubt” 64 times. The hash records all of it: $2b$, a two-digit cost, 22 characters of salt and 31 of result, in bcrypt’s own base64 alphabet (./A–Za–z0–9, not the usual one). The cost is an exponent, so each step up doubles the time: cost 10 is 1,024 rounds and cost 12 is 4,096. OWASP’s Password Storage Cheat Sheet asks for at least 10, the default here.

The limit people trip over is 72 bytes. The key schedule reads eighteen 32-bit words of password and never more, so everything after byte 72 is ignored — not rejected, ignored. It counts bytes of UTF-8, not characters: 72 ASCII letters fill it, but so do 36 Greek letters or 18 four-byte emoji. A longer password matches any other password with the same first 72 bytes, and when byte 72 falls inside a character only part of that character counts. This tool says when either happens. Go’s x/crypto/bcrypt and pyca/bcrypt 5.0 refuse to hash such passwords instead. A NUL character is refused here outright, because libraries disagree about it: the C implementations stop reading at the first one, while Go and Python hash straight through it.

The letter after $2 records bugs, not a different algorithm. In 2011 crypt_blowfish, the implementation in PHP and several Linux distributions, was found to mishandle bytes above 127 (CVE-2011-2483); the fixed version writes $2y$, and $2x$ marks hashes made with the bug on purpose so they can still be checked. In 2014 OpenBSD fixed a bug of its own — the password length was kept in 8 bits, so passwords of 255 bytes or more wrapped round — and introduced $2b$. For any text password all three prefixes give the same hash here; what differs is who accepts the label. PHP and Apache write $2y$, OpenBSD and pyca/bcrypt write $2b$, Go writes $2a$, and OpenBSD refuses $2y$. Both halves run in a Web Worker, so a high cost cannot freeze the page, and checking recomputes the hash and compares it in constant time.

Common problems

Every example below is run against this tool in our test suite, so what it says here is what the tool actually does.

The hash contains $$, which is Docker Compose’s escape for a single $.

$$2y$$05$$c4WoMPo3SXsafkva.HHa6uXQZWr7oboPiC2bT/r7q1BB8I2s0BRqC
Why:
Docker Compose reads $ as the start of a variable, so a bcrypt hash inside a compose file has to be written with every $ doubled. Copied back out of the file, the doubled signs come with it.
Fix:
Replace each $$ with a single $. Keep them doubled only inside the compose file itself.

A bcrypt hash is 60 characters long; this one is 29.

$2b$10$N9qo8uLOickgx2ZMRZoMye
Why:
The hash was cut short — usually by a database column narrower than 60 characters, or by a copy that stopped early. A hash that passed unquoted through a shell loses pieces too, because $2b, $10 and the rest are read as variables.
Fix:
Store bcrypt hashes in a column of at least 60 characters, and put them in single quotes on a command line.

$2x$ marks a hash deliberately computed with the pre-2011 crypt_blowfish bug (CVE-2011-2483).

$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e
Why:
Before version 1.1, crypt_blowfish sign-extended bytes above 127, so passwords with non-ASCII characters got the wrong hash. $2x$ exists so that such old hashes can still be checked with the bug reproduced; this tool computes only the correct algorithm.
Fix:
Check it on the system that wrote it, and rehash as $2y$ or $2b$ at the user’s next successful sign-in.

Two different long passwords both verify against the same hash.

Why:
bcrypt reads only the first 72 bytes of a password and ignores the rest, so two passwords that share their first 72 bytes are the same password to it. Multi-byte characters reach the limit sooner than the character count suggests.
Fix:
Cap passwords at 72 bytes, or pre-hash them as OWASP describes: HMAC the password with a secret pepper, base64 the result, and bcrypt that.

Sign-in became slow after raising the cost from 10 to 14.

Why:
The cost is an exponent. Each step doubles the work, so 14 is sixteen times slower than 10, on every sign-in — and the server pays it for wrong passwords too, which makes a high cost a denial-of-service lever.
Fix:
Raise the cost one step at a time and measure; OWASP’s rule of thumb is under a second per hash. Rehash each stored password at its next successful sign-in.

Frequently asked questions

Why does bcrypt ignore everything after 72 characters?
It is 72 bytes, not characters. The key schedule reads eighteen 32-bit words of password, which is 72 bytes, and never looks further, so the rest makes no difference to the hash. Accented letters take two bytes in UTF-8 and most emoji four, so the limit arrives sooner than it looks. This tool warns when a password passes it.
What is the difference between $2a$, $2b$ and $2y$?
For any text password, nothing: they compute the same hash. $2y$ is crypt_blowfish’s marker for hashes made after its 2011 fix, and $2b$ is OpenBSD’s for hashes made after its 2014 fix. The label matters only for what will accept it — PHP and Apache write $2y$, OpenBSD and Python’s bcrypt write $2b$, Go writes $2a$, and OpenBSD does not accept $2y$.
What bcrypt cost factor should I use?
At least 10, which is OWASP’s current minimum and the default here, and as high as your server can afford — OWASP suggests keeping a hash under a second. PHP 8.4 and Python’s bcrypt default to 12. Each step doubles the time, so measure on the machine that will do the checking rather than in a browser.
Is it safe to type a real password into this page?
The hashing and checking run in a Web Worker in your browser, and there is no endpoint on this site that accepts a password, so nothing you type is sent anywhere. The salt comes from your browser’s cryptographic random source.

Last updated