← Back to Blog

bcrypt: Why It Outlasted MD5-Crypt and SHA-Crypt

bcrypt: Why It Outlasted MD5-Crypt and SHA-Crypt - QNSQY post-quantum encryption guide

In 1999, Niels Provos and David Mazieres published a paper titled "A Future-Adaptable Password Scheme." The paper proposed bcrypt, a password hashing function that would remain useful even as computers got faster, by letting administrators dial up the cost factor over time. More than 25 years later, bcrypt is still in active production use, protecting passwords for many of the largest websites on the internet. The MD5-Crypt and SHA-Crypt schemes that competed with bcrypt in the early 2000s have largely been retired or are considered legacy. Argon2id has emerged as the next-generation standard. But bcrypt persists, often as the safe default in language standard libraries and password storage frameworks. Looking at why bcrypt has lasted so long, what its specific design choices were, and where it now sits in the hierarchy of password hashing functions, is a useful tour of how a deliberately slow algorithm can stay relevant for a quarter century.

The Password Hashing Problem in 1999

To understand why bcrypt was designed the way it was, you have to picture the password storage situation in the late 1990s. Web applications were storing passwords in databases. Many were storing them in plaintext, which was obviously bad. Some were using MD5 or SHA-1 to hash them, which was better but still vulnerable to dictionary attacks. A few used MD5-Crypt, a scheme designed for Unix password files that ran 1000 iterations of MD5.

The fundamental problem was that hash functions like MD5 and SHA-1 were designed to be fast. Computers were getting faster every year. An attacker with a modern computer could compute billions of MD5 hashes per second by the early 2000s, which made dictionary attacks against MD5-Crypt much faster than the designers had anticipated.

Provos and Mazieres looked at the situation and asked: what would a password hashing scheme look like if it was designed to remain useful for decades, including against attackers with future hardware? Their answer had two parts.

First, the scheme should be deliberately slow. Slow enough that even the legitimate user notices the cost when verifying their password, which forces an attacker to incur the same cost for every guess.

Second, the slowness should be tunable. The administrator should be able to set a cost factor that determines how many iterations the algorithm runs. As hardware gets faster, the cost factor can be increased to keep the running time roughly constant.

The result was bcrypt.

How bcrypt Works

bcrypt is built on a modified version of the Blowfish block cipher. The Blowfish key schedule, the procedure for setting up the cipher's internal subkey tables from the user's key, is computationally expensive. Provos and Mazieres took advantage of this by using a modified Blowfish key schedule, called EksBlowfish (Expensive key schedule Blowfish), as the core of bcrypt.

The bcrypt algorithm works in three steps:

First, derive an initial key state from the password and salt using the EksBlowfish key schedule. This step runs the schedule a number of times determined by the cost factor.

Second, encrypt a fixed magic string ("OrpheanBeholderScryDoubt") 64 times using the initial key state. The repeated encryption produces the bcrypt output.

Third, encode the salt, cost factor, and the encrypted output into the standard bcrypt string format, which looks like "$2a$12$<22-char-salt><31-char-hash>".

The cost factor in bcrypt is a logarithmic value. A cost factor of 10 means 2^10 = 1024 iterations of the EksBlowfish key schedule. A cost factor of 12 means 2^12 = 4096 iterations. Each increase by 1 doubles the running time. Modern recommendations are cost factors of 12 or higher, depending on the hardware.

For comparison with the predecessor and successor functions see PBKDF2 Explained and Argon2id Explained.

Why bcrypt Outlasted Its Competitors

Several password hashing schemes existed alongside bcrypt in the 2000s. The main competitors were MD5-Crypt and SHA-Crypt (both SHA-256 and SHA-512 variants).

MD5-Crypt was developed for FreeBSD and Linux Unix password files. It ran 1000 iterations of MD5 with a salt. By the late 2000s, GPU-accelerated MD5 hashing made MD5-Crypt fast to brute-force, even with the iteration count.

SHA-Crypt was Ulrich Drepper's response to the weakness of MD5-Crypt. It ran a configurable number of iterations of SHA-256 or SHA-512 with a salt. SHA-Crypt was technically sound but suffered from the same problem as PBKDF2: it was purely computational, not memory-hard. Custom hardware could compute SHA hashes much faster than general-purpose CPUs.

bcrypt outlasted both for two reasons. First, bcrypt's EksBlowfish key schedule is intrinsically harder to optimize on GPUs than MD5 or SHA. The key schedule involves frequent random memory access patterns within a 4 KB internal state, which fits poorly into GPU memory architectures. GPU-accelerated bcrypt cracking is faster than CPU bcrypt, but the speedup is much smaller than for MD5-Crypt or SHA-Crypt.

Second, bcrypt was widely adopted in web frameworks. Ruby on Rails, Django, PHP, Node.js, and many other web platforms shipped with bcrypt as the recommended password hashing function. The widespread availability and the safe defaults in popular frameworks meant bcrypt got deployed in many places where developers might otherwise have made worse choices.

bcrypt's Memory and Hardware Resistance

The EksBlowfish state is 4 KB, distributed across the cipher's S-boxes and P-array. This 4 KB working set is small by modern standards but is still significantly larger than the state of MD5 or SHA-256, which is just a few hundred bytes.

The 4 KB state, combined with the access pattern of the key schedule, makes bcrypt somewhat resistant to GPU acceleration. GPUs are optimized for streaming computation with predictable memory access. bcrypt's frequent updates to the 4 KB state, with access patterns that depend on the password and salt, do not parallelize well across GPU cores. A typical GPU can run thousands of bcrypt instances in parallel, but each instance is only a few times faster than a CPU instance, not orders of magnitude faster.

This was an important property in the 2000s and 2010s. As GPU mining of cryptocurrencies pushed the development of high-performance hashing hardware, MD5 and SHA-256 attack rates increased by several orders of magnitude. bcrypt attack rates increased much more slowly. The asymmetry between defender and attacker stayed manageable.

By the 2020s, the picture is more nuanced. Custom ASIC hardware can attack bcrypt faster than GPUs can, although still not as effectively as it can attack MD5 or SHA. The rise of memory-hard functions like scrypt and Argon2id changed the landscape further: memory-hard functions are intrinsically much more resistant to custom hardware than bcrypt because the memory requirement scales with the work, not just the time.

For the memory-hard story see Argon2id Explained.

Cost Factor Calibration

The bcrypt cost factor needs periodic recalibration. The right value depends on the year, the hardware, and the threat model.

Provos and Mazieres' original 1999 paper recommended cost factors that gave roughly 0.3 seconds of computation on hardware of the time, which was a cost factor of around 6 to 8.

By 2010, hardware was much faster. Recommended cost factors had moved to 10 or 11, giving roughly 0.1 to 0.5 seconds of computation on contemporary hardware.

By 2020, recommended cost factors were 12 or higher. The OWASP Password Storage Cheat Sheet currently recommends cost 10 or higher, with cost 12 being a common modern choice.

The practical constraint on cost factor is user-perceived latency. If logging in takes more than a second, users notice. Most production deployments target around 200 to 500 milliseconds per login, which on modern server CPUs translates to cost factors of 11 or 12.

The cost factor is encoded in the bcrypt output string, which means existing password hashes can be verified at any cost factor. New passwords get the current recommended cost. This allows gradual migration: when a user logs in, the system can check whether their stored hash uses an outdated cost factor, and if so, rehash with a new cost on successful login.

Where bcrypt Is Used Today

bcrypt is the default password hashing function in many modern web frameworks. Ruby on Rails uses bcrypt through the bcrypt gem and the has_secure_password method. Django uses bcrypt as one of the recommended hashers. PHP includes bcrypt through password_hash() with PASSWORD_BCRYPT. Node.js has the bcrypt and bcryptjs packages.

Major websites that store passwords with bcrypt include LinkedIn, Twitter, Yahoo, and many others. Some have migrated to Argon2id. Some are still on bcrypt with high cost factors.

Password manager services typically use a memory-hard function like Argon2id or scrypt for the master password derivation, because the master password is the most sensitive credential and the user only goes through that derivation occasionally. For per-service password hashing, bcrypt is still common.

In the FIPS 140-3 compliance space, bcrypt is not directly approved. NIST FIPS standards for password hashing focus on PBKDF2. For applications that need FIPS compliance, PBKDF2 with high iteration counts is the right choice. For applications that do not need FIPS compliance, bcrypt or Argon2id are typically better choices.

bcrypt and Post-Quantum Cryptography

bcrypt is built on Blowfish, which is a 64-bit block cipher. The 64-bit block size has the same Sweet32 issue as 3DES and other 64-bit ciphers, which means high-volume encryption with Blowfish is unsafe. However, bcrypt does not use Blowfish for encryption: it uses the EksBlowfish key schedule, and the 64-bit block size is not relevant to the password hashing security.

The post-quantum impact on bcrypt is limited to Grover's algorithm halving the effective entropy of the password. If the user picks a 50-bit-entropy password, the effective post-quantum entropy is 25 bits, which is dangerously low. The bcrypt cost factor mitigates this by making each guess expensive, but the fundamental issue is the password entropy itself.

The fix is to use a strong password, ideally generated by a password manager. With a 128-bit-entropy random password, the effective post-quantum entropy is 64 bits, which combined with bcrypt's cost factor is still acceptable.

For users who rely on memorable passwords, the right approach is to use a password manager with a single high-entropy master password and let the manager generate strong random passwords for each service. The master password derivation should use a memory-hard function like Argon2id, and the per-service passwords stored in the manager can use whatever the service's hashing infrastructure supports.

For the post-quantum context see What Is Post-Quantum Cryptography and Harvest Now, Decrypt Later.

When to Use bcrypt vs Argon2id

The Argon2 family won the Password Hashing Competition in 2015 and was published as IETF RFC 9106 in 2021. Argon2id is the recommended variant for general-purpose password hashing. It provides memory-hardness, which gives stronger resistance to custom hardware attacks than bcrypt.

The choice between bcrypt and Argon2id today depends on several factors:

Argon2id is the better choice for new applications that have full control over the runtime environment. It provides stronger security against attackers with custom hardware.

bcrypt is the better choice when the runtime environment cannot support Argon2id's memory requirements. Some shared hosting environments, some constrained embedded systems, and some serverless platforms have CPU and memory limits that make Argon2id impractical.

bcrypt is also the right choice when migrating from a legacy system that already uses bcrypt. The migration to Argon2id can be done gradually as users log in, but it is not urgent if the existing bcrypt cost factor is high enough.

For frameworks that ship with bcrypt as the default, bcrypt is acceptable. The framework defaults are usually correct.

How QNSQY Approaches Password Hashing

QNSQY uses Argon2id for password-based key derivation in its file encryption operations. The reasoning is that QNSQY's threat model includes attackers who might invest in custom hardware, and Argon2id's memory-hardness provides better resistance than bcrypt or PBKDF2.

The QNSQY billing API, running on Cloudflare Workers, uses PBKDF2 with 100,000 iterations because the Workers CPU and memory limits do not accommodate Argon2id parameters. This is a constrained-environment exception. For the main file encryption operations, where QNSQY runs in the user's local environment with full access to memory and CPU, Argon2id is the right choice.

bcrypt does not appear in the QNSQY stack. The reason is consistency: Argon2id is the right modern default for password hashing, and adding bcrypt as an additional option would create implementation surface without providing security benefits.

For the broader QNSQY architecture see Hybrid Encryption, AES-256-GCM Explained, and Argon2id Explained.

Frequently Asked Questions

Is bcrypt secure? Yes. bcrypt has been in production use for more than 25 years and has resisted serious cryptanalytic attack. The cost factor can be increased to keep pace with hardware improvements. Modern recommendations are cost factors of 12 or higher.

Should I use bcrypt or Argon2id? For new applications with full control over the runtime environment, Argon2id is the modern recommendation. For applications that need to work in constrained environments or that already use bcrypt, bcrypt is acceptable with a high cost factor.

What cost factor should I use? As of 2026, OWASP recommends cost factor 10 or higher. Many production deployments use cost 12. The right value gives 200 to 500 milliseconds of computation per login on the deployment hardware. Recalibrate every few years as hardware improves.

Does bcrypt have a maximum password length? Yes, the original bcrypt implementation truncates passwords at 72 bytes. Some implementations pre-hash longer passwords with SHA-256 or SHA-512 before passing them to bcrypt, which works around the limit. Most users have passwords well under 72 bytes, so this is rarely a practical issue.

Is bcrypt approved by NIST FIPS? No. NIST FIPS 140-3 approved password hashing focuses on PBKDF2. For applications that need FIPS compliance, PBKDF2 with high iteration counts is the right choice. Argon2id is not yet FIPS-approved either, although NIST is considering it.

Sources

  1. Provos, N. and Mazieres, D. "A Future-Adaptable Password Scheme." USENIX 1999. https://www.usenix.org/legacy/event/usenix99/provos/provos_html/
  2. OWASP. "Password Storage Cheat Sheet." 2025 update. https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
  3. IETF RFC 9106. "Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications." 2021. https://datatracker.ietf.org/doc/html/rfc9106
  4. IETF RFC 8018. "PKCS #5: Password-Based Cryptography Specification Version 2.1." 2017. https://datatracker.ietf.org/doc/html/rfc8018
  5. NIST SP 800-132. "Recommendation for Password-Based Key Derivation, Part 1: Storage Applications." 2010. https://csrc.nist.gov/pubs/sp/800/132/final
  6. Schneier, B. "Description of a New Variable-Length Key, 64-Bit Block Cipher (Blowfish)." Fast Software Encryption 1993. https://www.schneier.com/academic/paperfiles/paper-blowfish-fse.pdf

Related Articles

Protect Your Data Before Q-Day Arrives

QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.

Try QNSQY