πŸ” BCrypt Hash Explainer

Paste any BCrypt hash to visually decode its every segment β€” version, cost factor, salt and digest β€” with color-coded annotations, timing analysis and deep-dive educational content.

BCrypt Hash Input
Samples:
Full Hash Anatomy

BCrypt Quick Reference

Hash Format

A BCrypt hash is always exactly 60 characters : 4-char prefix ( $2b$ ), 2-digit cost, $ separator, 22-char Base64 salt, 31-char Base64 digest. No separators between salt and digest β€” the split is positional at character 29 from the cost $ .

72-Byte Limit

BCrypt silently truncates passwords to 72 bytes. "password123456…" (73+ chars) hashes identically to the first 72 chars. Mitigation: pre-hash with SHA-256/512 before BCrypt, or use Argon2/scrypt. Some libraries (like bcryptjs) warn about this.

BCrypt Base64

BCrypt uses a custom Base64 alphabet: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 β€” not the standard RFC 4648 alphabet. The output is not decodable with standard atob() or standard Base64 libraries without remapping.

Why Not SHA-256?

SHA-256 runs at billions of hashes per second on modern GPUs (A100: ~100 GH/s). A BCrypt cost 12 hash takes ~250ms β€” roughly 25 billion times slower. An attacker cracking SHA-256 in 1 second would need 25 billion seconds (>790 years) with BCrypt cost 12.

Bcrypt vs. The Alternatives β€” Choosing a Password Hashing Algorithm in 2024
Bcrypt is 26 years old and still widely recommended. Here's how it compares to the algorithms that came after it and when you should consider switching.
AlgorithmYearMemory hardnessMax password lengthCurrent recommendation
bcrypt1999No72 bytes (silently truncates)Still solid for most web apps β€” wide library support, 25 years of audits, default in many frameworks
scrypt2009YesUnlimitedGood β€” memory-hard defeats GPU attacks better; used by some Linux distros for /etc/shadow
Argon2i2015 (PHC winner)YesUnlimitedBest for password hashing β€” side-channel-resistant variant; OWASP #1 recommendation since 2019
Argon2id2015 (PHC winner)YesUnlimitedBest overall β€” hybrid of Argon2i and Argon2d; OWASP primary recommendation for new systems
PBKDF2-SHA2562000NoUnlimitedAcceptable β€” FIPS-approved; required in US government contexts; needs 600,000+ iterations in 2024
MD5 / SHA-256 (bare)β€”NoUnlimitedNever β€” nanosecond hashes, billions per second on GPU, no protection at all
Things That Catch Developers Out With Bcrypt
Bcrypt is easy to get mostly right, and those last few percent matter.
72-byte truncation is silent and dangerous

Bcrypt hashes only the first 72 bytes of the input. A password of 73 bytes and a password of 72 bytes that share the same first 72 bytes hash identically. This means someone whose password is 100 characters long effectively has a 72-character password β€” without being told. Mitigate by pre-hashing: bcrypt(base64(sha256(password))) before passing to bcrypt. This converts an arbitrarily long password into a fixed-length, full-entropy input.

Cost factor 10 isn't enough anymore

Bcrypt was designed in 1999 for hardware that was 10,000Γ— slower. Cost factor 10 meant ~100ms per hash then. On a modern server, cost 10 takes ~10ms. OWASP currently recommends cost 12 as a minimum, targeting 250ms+ per hash on your production hardware. Run a benchmark on your own server and pick the cost factor that keeps login at 250–500ms. Bump it every few years as hardware improves.

bcrypt output is the salt + hash combined

Unlike SHA-256 where you store the salt separately, bcrypt embeds a random 128-bit salt directly in the 60-character hash string: $2b$12$[22-char salt][31-char digest]. This means you only store one string per user, not a hash + salt pair. The verification function parses the embedded salt automatically. Never strip or separate the parts β€” the whole string is the stored credential.

$2a vs $2b vs $2y β€” which version prefix?

$2a$ had a bug in some implementations (incorrect handling of 8-bit chars). $2b$ (2011) is the corrected standard. $2y$ is PHP's equivalent of $2b$. For new code, always use a library that generates $2b$. Libraries that still generate $2a$ are outdated. All modern bcrypt implementations can verify hashes with any prefix.

Don't bcrypt the same password twice

Some developers pre-hash with SHA-256 and then also apply bcrypt β€” which is fine if done correctly. But re-running bcrypt on an already-bcrypt-hashed value (e.g., when updating cost factor) produces a double-bcrypt hash that's extremely difficult to manage. To update cost factors for existing users, re-hash only on their next successful login when you have the original password.

Pepper adds defence in depth β€” but use it carefully

A "pepper" is a site-wide secret mixed into password hashes (separate from the per-user salt). If an attacker steals your database, the pepper stored in application config means they can't crack hashes without also stealing the config. Implement it as a pre-hash step: HMAC-SHA256(pepper, password) before bcrypt. Store the pepper version number alongside the hash so you can rotate it without invalidating all hashes.

Related Tools
Other free security and hashing tools on IndexCraft.
Questions About Bcrypt
Bcrypt is built on the Blowfish cipher's expensive key schedule (EksBlowfish). The algorithm is specifically designed as a one-way function β€” given a bcrypt hash, there is no mathematical shortcut to the original password. The only practical attack is trial and error: guess a password, bcrypt it with the same salt and cost factor, compare. The deliberate slowness (250–500ms per attempt) makes this computationally expensive even on specialised hardware.
Yes, every time. Bcrypt generates a new cryptographically random 128-bit salt on each hash operation. Two bcrypt hashes of "password123" will look completely different from each other. This is intentional β€” it means an attacker can't pre-compute a rainbow table. To verify a password, you don't compare hash strings directly; you re-run bcrypt with the salt extracted from the stored hash and see if the outputs match. Every bcrypt library's verify() function handles this automatically.
Existing hashes remain valid at their original cost factor β€” the cost factor is embedded in the hash string and verified accordingly. Hashes generated before the update will continue to work. To migrate existing users to the higher cost factor, re-hash their password on their next successful login: verify with the old hash, then store a new hash with the updated cost factor. Over time, all active users' hashes will be upgraded. You can query for hashes with the old cost factor to see how many remain.
Less so than SHA-based algorithms, but not immune. Bcrypt's internal state requires significant memory bandwidth and is not easily parallelised on GPU architectures in the same way SHA is. Modern GPUs can still run millions of bcrypt attempts per second at cost 10, but that's orders of magnitude fewer than the billions they achieve with SHA-256. This is why cost factor escalation and memory-hard alternatives (Argon2id) continue to matter β€” Argon2id's configurable memory requirement makes GPU parallelism expensive in a way bcrypt doesn't.
For a running system: opportunistically, not all at once. Argon2id (especially with high memory settings) provides better protection against GPU and ASIC cracking than bcrypt, and is the OWASP primary recommendation for new systems. However, bcrypt at cost 12+ remains acceptable β€” you're not in urgent danger if you're using bcrypt correctly. The migration path is the same as cost-factor upgrades: detect the algorithm from the hash prefix, verify with the old algorithm on login, re-store with Argon2id. Plan for it on your next major auth refactor rather than as an emergency.