Command Palette

Search for a command to run...

How Bcrypt Works: Salt, Cost Factor and Limits

How Bcrypt Works: Salt, Cost Factor and Limits

T
Toolz Team
|Sep 13, 2026|17 min letto

Parte della raccolta sicurezza

Generatore Bcrypt

Genera e verifica gli hash delle password bcrypt nel browser con un fattore di costo regolabile

Usa Generatore Bcrypt

The first time I shipped a login system, I did the thing every tutorial from that era told me to do: stored md5($password) in the users table and moved on. It worked, and it was quietly a disaster. Years of building auth into WordPress plugins and Laravel apps later, I have a much healthier respect for the gap between "the password check passes" and "the passwords are safe if this database leaks." Bcrypt is the tool that closes most of that gap, and it is the default in almost every framework I reach for. So I built a bcrypt generator on toolz.dev that hashes a password with a cost factor you control, verifies a password against an existing hash, and does all of it in your browser so nothing you type is ever sent anywhere. This is the guide to it and to bcrypt itself.

TL;DR: Bcrypt turns a password into a salted, deliberately slow 60-character hash so a leaked database is not a leaked password list. The Toolz bcrypt generator hashes any password with an adjustable cost factor (4 to 31), verifies a password against a hash you paste, and shows the version, cost, and salt it parsed. It produces the standard $2b$ format that OpenBSD, PHP password_hash, Laravel, and Node bcrypt libraries all accept, and it runs entirely client-side.

What is bcrypt?

Bcrypt is a password-hashing function designed by Niels Provos and David Mazieres and presented in their 1999 USENIX paper "A Future-Adaptable Password Scheme". It is built on the Blowfish block cipher, but with a twist: instead of encrypting data, it runs Blowfish's key-setup step over and over, using the password and a random salt as the key material. The result is a one-way hash that is cheap to compute once and expensive to compute a billion times, which is exactly the property password storage needs.

That "expensive on purpose" idea is the heart of it. A general-purpose hash like SHA-256 is engineered to be fast, because checksums and integrity checks want speed. Password hashing wants the opposite. If an attacker steals your database, the only thing standing between them and every plaintext password is how long it takes to guess. A fast hash lets a modern GPU test billions of candidates per second; a bcrypt hash at a sensible cost caps that at a few thousand per second per core. Same leaked file, wildly different outcome.

Bcrypt has survived more than two decades because it got the fundamentals right and because it is adaptable. The cost factor means the same algorithm that was tuned for 1999 hardware can be dialled up for 2026 hardware without changing anything else. It remains a recommended option in the OWASP Password Storage Cheat Sheet, and it is the out-of-the-box default in Laravel, Spring Security, and many other stacks I work in. If you want the deeper background on why fast hashes are wrong for this job, the data privacy online tools guide covers the reasoning that pushed the whole industry toward slow, salted hashing.

What does a bcrypt hash contain?

One of the nicest things about bcrypt is that its output is self-describing. Take a real hash:

$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW

Read left to right, split on the dollar signs, and it tells you everything needed to verify a password against it:

Field Example Meaning
Version 2b The bcrypt variant. 2b is the current corrected OpenBSD format
Cost 12 The work factor. The key-setup runs 2^12 = 4096 iterations
Salt R9h/cIPz0gi.URNNX3kh2O 22 characters of custom Base64, decoding to 16 random bytes
Digest PST9/PgBkqquzi.Ss7KIUgO2t0jWMUW 31 characters encoding the 23-byte bcrypt output

Because the salt and cost live inside the string, verification needs nothing else. When you paste a hash into the bcrypt generator verify mode, it reads the cost and salt straight out of the string, recomputes the digest with that exact salt, and compares. There is no separate salt column to manage, which is one reason bcrypt is so pleasant to store: it is a single opaque field in your users table.

The custom Base64 alphabet is worth a note, because it trips people up. Bcrypt uses ./A-Za-z0-9, which is a different ordering from standard Base64. A bcrypt salt is not a standard Base64 string, so do not try to decode it with a generic Base64 tool and expect meaningful bytes. If you are curious about the standard variant used everywhere else, the base64 encoding guide walks through the normal alphabet and where it shows up.

How do I generate a bcrypt hash?

In the tool it is three steps: type a password, pick a cost, click Generate. Under the hood, generating a hash does more than it looks. First a cryptographically strong random salt of 16 bytes is created using the Web Crypto API. Then bcrypt runs its expensive key schedule, seeding Blowfish with the password and salt and iterating 2^cost times. Finally it encrypts a fixed magic string ("OrpheanBeholderScryDoubt") 64 times with the resulting cipher state and Base64-encodes the output. That whole dance produces the 60-character hash.

The single most common surprise is that hashing the same password twice gives two different hashes. That is correct and intentional. Each hash gets its own random salt, so two users who both pick hunter2 end up with completely different stored values. An attacker who steals the table cannot tell that two accounts share a password, and cannot reuse a precomputed table across accounts. If you want to reproduce a specific hash for testing, you supply the full salt string rather than a bare cost, which is exactly what verify mode does internally.

In your application code you never assemble any of this by hand. You call the library:

// PHP
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// Node with bcrypt / bcryptjs
const hash = await bcrypt.hash(password, 12)
// Laravel
use Illuminate\Support\Facades\Hash;
$hash = Hash::make($password); // bcrypt by default

The Toolz tool is for the moments around that code: sanity-checking what a hash looks like at a given cost, generating a known hash for a seeder or a test fixture, or confirming that a hash you found in a config matches the password you think it does.

How do I choose a cost factor?

The cost factor is the one knob you have to think about, and the honest answer is "measure, do not copy." The rule of thumb from OWASP is that a single hash should take somewhere around a quarter of a second on your production hardware. That target balances two opposing forces: too low and you make cracking cheap, too high and every legitimate login pays the price, which matters when a busy endpoint is hashing on every request.

Because the cost is a power of two, each step doubles the work. Cost 11 is twice as slow as cost 10, cost 12 is four times as slow. On typical 2020s server hardware, cost 10 to 12 lands in the right neighbourhood, which is why frameworks default there. The tool shows a rough time estimate as you drag the slider so you can feel how quickly the cost compounds, but treat that as a relative guide, not a benchmark of your server. The real number depends on your CPU, and the only way to know it is to time password_hash on the box that will run it.

One practical pattern worth knowing: because the cost is stored inside every hash, you can raise it over time without a migration. On a successful login you have the plaintext in hand for a moment, so you check whether the stored hash's cost is below your current target and, if so, rehash and update. PHP exposes this directly through password_needs_rehash. It means your security keeps pace with hardware without ever forcing a password reset.

Bcrypt versus SHA-256, Argon2, and scrypt

The comparison that matters most is bcrypt against a plain fast hash, and against the newer memory-hard functions. Here is how they line up for password storage:

Function Salted Slow on purpose Memory-hard Good for passwords?
MD5 / SHA-256 No (by itself) No No No, far too fast
bcrypt Yes, built in Yes, tunable cost No Yes, recommended
scrypt Yes Yes Yes Yes
Argon2id Yes Yes Yes Yes, preferred for new systems

A bare SHA-256 of a password, even salted, is the wrong tool: it is designed for speed, so a leaked database of salted SHA-256 hashes falls quickly to a GPU. If you only need a fast digest for a checksum or a cache key rather than a password, that is what the hash generator is for, and the distinction is the whole point. Argon2id, the winner of the 2015 Password Hashing Competition, adds memory-hardness, which resists the custom hardware that can accelerate bcrypt, and it is the first choice for a brand-new system where you control the runtime. But bcrypt is not obsolete. It is battle-tested, it is the default in tools I use daily, and choosing it is a sound decision, especially when your framework ships it. The mistake to avoid is not "bcrypt instead of Argon2," it is "a fast hash instead of any of them." For a broader tour of the security utilities that pair with hashing, the developer productivity tools guide has the map.

What about the 72-byte limit and other gotchas?

Bcrypt only reads the first 72 bytes of the password. Anything past that is silently ignored, which for normal passwords never matters but can bite a very long passphrase, especially one with multi-byte UTF-8 characters where the byte count climbs faster than the character count. The tool warns you whenever your input crosses 72 UTF-8 bytes so the truncation is never a silent surprise. Some systems work around the limit by pre-hashing the password with SHA-256 and Base64-encoding it before handing it to bcrypt, which folds an arbitrarily long password into a fixed short input; if you do this, do it consistently on both sign-up and login.

The other gotcha is the version prefix. You will see $2a$, $2y$, and $2b$ in the wild. $2a$ was the original, $2y$ was a PHP-specific tag introduced after a sign-extension bug was found in some implementations, and $2b$ is the current corrected OpenBSD version. For new hashes you want $2b$, which is what this tool produces. All three verify identically for ordinary ASCII passwords; the differences only surface with particular non-ASCII bytes near the 72-byte boundary. When you paste a token into a tool that also inspects structured credentials, like the JWT decoder, keep in mind that a bcrypt hash is not a token you decode to read a payload; it is a one-way digest with nothing to reveal.

Where the bcrypt generator fits in a real workflow

On its own a hash is only half the story, so the mixer sits alongside the other credential tools I keep open. When I am seeding a test database or setting up a demo account, I generate the password itself with the password generator, check that it clears a sensible strength bar with the password strength checker, and only then run it through the bcrypt generator to get the stored hash. That order matters: a strong random password behind a cost-12 bcrypt hash is genuinely hard to crack, while a weak password behind the same hash still falls to a dictionary attack, because the attacker only has to guess the handful of likely candidates rather than the whole keyspace. The cost factor buys you time; the password entropy is what that time is spent against.

The verify mode earns its place during debugging. When a login mysteriously fails in staging, the fastest way to isolate the problem is to paste the stored hash and the password you believe is correct and see whether they match. A match means the bug is upstream, in how the password reaches the check; a mismatch means the stored hash was written with a different password or a mangled encoding. Either answer saves you from guessing, and because the whole thing runs locally, you can do it with a real hash pulled from a database without that hash ever leaving your machine. It is the same client-side principle behind every tool on the site, and for anything touching credentials it is not a nicety, it is the requirement.

Frequently asked questions

Is it safe to hash a real password in this bcrypt generator?

Yes, because the hashing happens entirely in your browser and nothing is sent anywhere. You can confirm this in the Network tab of your developer tools: generating a hash makes no request. That said, a browser tab is not a password manager, so avoid pasting the live credentials of a production account into any web page you did not build, and close the tab when you are done.

Why does the same password produce a different bcrypt hash every time?

Because bcrypt generates a new random salt for every hash and mixes it into the result. This is a feature: two users who choose the same password still get different stored hashes, so an attacker cannot spot shared passwords or reuse a precomputed table. Verification still works because the salt is stored inside the hash string itself.

What cost factor should I use for bcrypt?

Use the highest cost your login flow can tolerate without an annoying delay, which for most 2020s servers is cost 10 to 12. OWASP suggests a single hash should take roughly a quarter of a second on your production hardware. Because each step doubles the work, measure on your own server rather than copying a number, and raise the cost every few years as hardware speeds up.

Can a bcrypt hash be reversed back to the password?

No. Bcrypt is a one-way function, so there is no mathematical way to turn a hash back into the original password. The only attack is guessing: an attacker hashes candidate passwords with the same salt and cost and checks for a match. That is why the cost factor matters, because it sets how expensive each guess is, and why strong unique passwords plus a high cost make guessing impractical.

Is bcrypt still secure in 2026, or should I switch to Argon2?

Bcrypt is still considered secure for password storage and remains a recommended option in the OWASP Password Storage Cheat Sheet. Argon2id, the winner of the 2015 Password Hashing Competition, is preferred for new systems where you control the runtime because it also resists GPU and specialised-hardware attacks. Bcrypt is a sound choice, especially when your framework ships it as the default; the real mistake is using a fast general-purpose hash instead of any password-hashing function.

What is the 72-byte limit in bcrypt?

Bcrypt only reads the first 72 bytes of the UTF-8 encoded password, so any characters beyond that are ignored. For normal passwords this never matters, but a very long passphrase can be truncated, which is why some systems pre-hash the password with SHA-256 before bcrypt. This tool warns you whenever your input exceeds 72 bytes so the truncation is never a surprise.

What do the $2a$, $2b$, and $2y$ prefixes mean?

They are version tags for the bcrypt output format. $2a$ was the original, $2y$ was a PHP-specific fix for a sign-extension bug, and $2b$ is the current corrected OpenBSD version. For new hashes $2b$ is correct, which is what this tool produces. All three verify identically for ASCII passwords; the differences only appear with certain non-ASCII bytes at the 72-byte boundary.

Is bcrypt the same as encryption?

No. Encryption is reversible with a key, so encrypted data can be decrypted back to the original. Bcrypt hashing is one-way and deliberately slow, and it is meant for verification only: you never need to recover the password, only to confirm someone typed the right one. Storing passwords with encryption instead of hashing is an anti-pattern, because whoever holds the key can recover every password.

Comments

0 comments

0/2000 characters

No comments yet. Be the first to share your thoughts!