MD5, SHA-256 and bcrypt all turn input into a fixed-looking string of characters, so they often get lumped together as "hashing." But they were built for different jobs, and picking the wrong one is how password databases end up cracked.
The quick version: MD5 and SHA-256 are fast, general-purpose hash functions. bcrypt is a password hashing function, deliberately slow and salted. Speed is a feature in the first case and a vulnerability in the second.
The short answer
| Use case | MD5 | SHA-256 | bcrypt |
|---|---|---|---|
| Storing passwords | No | No | Acceptable, but Argon2id is preferred |
| File checksums against accidental corruption | Works | Yes | No |
| Integrity where someone might tamper on purpose | No | Yes | No |
| Digital signatures, certificates | No | Yes | No |
| Message authentication (as HMAC) | Legacy only | Yes (HMAC-SHA-256) | No |
| Cache keys, deduplication, non-security IDs | Works | Yes | No |
The rest of this article explains the reasoning behind each row.
What MD5 and SHA-256 are designed to do
A cryptographic hash function takes any input and produces a fixed-size digest: 128 bits for MD5, 256 bits for SHA-256. The same input always gives the same digest, and a tiny change in the input gives a completely different one. Here is SHA-256 for two passwords that differ by one character, computed the same way as our SHA Generator:
sunflower42 → 4568199f85cf84263ed16d17f400fc7f68ecf69387abccbe10c08a186ceeb5af
sunflower43 → fb6bd25358290a039fc6f226edf98519eb1d16c70253f5d4759d553fa09d9111
These functions are meant to be fast, because they're used to check downloads, sign documents and verify every packet in a TLS connection. Being fast is exactly what you want there.
Why MD5 is retired for security
A secure hash must make it practically impossible to find two different inputs with the same digest, a property called collision resistance. MD5 lost that property: researchers published practical collisions in 2004, and attacks improved from there. RFC 6151 concludes that MD5 is "no longer acceptable where collision resistance is required such as digital signatures."
That doesn't make MD5 useless. It still detects accidental corruption fine, and it's still common for cache keys and checksums where nobody is attacking you. Our MD5 Generator exists for exactly those jobs, and for matching checksums published by older software. Just don't use it anywhere an attacker could benefit from creating a collision.
SHA-1 went the same way later. NIST announced in 2022 that SHA-1 should be phased out by December 31, 2030, in favor of the SHA-2 and SHA-3 families. SHA-256 is part of SHA-2 and has no known practical attacks.
Why fast hashes are wrong for passwords
When a password database leaks, attackers don't reverse the hashes. They guess: take a candidate password, hash it, compare. The faster the hash, the more guesses per second, and general-purpose hashes run extremely fast on ordinary graphics cards.
The OWASP Password Storage Cheat Sheet is blunt about it: "Fast hashing algorithms such as SHA-256 are not suitable for password storage." MD5 is worse still, since it's faster.
There's a second problem. Unsalted hashes are identical for identical passwords. Here's MD5 for sunflower42:
9a8584b6d9f445a176c092ccc3c11dcb
Every system that stores plain MD5 stores that exact string for that password. So an attacker can precompute digests for millions of common passwords once and look them up instantly, and can see at a glance which users share a password.
How bcrypt is different
bcrypt was designed in 1999 specifically for passwords. It fixes both problems:
- It's salted. Each hash includes a random 128-bit salt, so the same password produces a different hash every time. Precomputed tables are useless.
- It's slow on purpose. A cost factor sets how much work each hash takes. Each step up doubles the work, so you can keep raising it as hardware gets faster.
Here's sunflower42 hashed twice at cost 12 with bcryptjs 3.0.3, the library our Bcrypt Generator uses:
$2b$12$wwY4sVt6blWkRD87ENHZmOqZPTsjRN5a6f5bAWX36wRkdq48DS7py
$2b$12$i6vgzCayeTdR61HaxXrUIOAScFt0sESPfsdXiiAyfThaFC5z.Pwea
Different strings, but both verify as correct for sunflower42, and both reject sunflower43. Reading the first one left to right:
$2b$: the bcrypt version12$: the cost, meaning 212 (4,096) rounds of the expensive key setupwwY4sVt6blWkRD87ENHZmO: the 22-character salt- The remaining 31 characters: the hash itself
Everything needed to verify a password is in that one string, so you store it in a single column and pass it back to your library's verify function.
bcrypt's 72-byte limit
bcrypt only uses the first 72 bytes of the password. Anything after that is silently ignored, so two long passwords that share their first 72 bytes produce matching hashes. OWASP recommends enforcing a maximum password length of 72 bytes when you use bcrypt. Note that the limit is in bytes, not characters: many non-English characters and emoji take 2 to 4 bytes each in UTF-8.
A tempting workaround is to hash the password with SHA-256 or SHA-512 first and feed that to bcrypt. OWASP calls this approach dangerous: raw digest bytes can contain null bytes that some bcrypt implementations treat as the end of the input, and it opens the door to "password shucking," where hashes leaked from another site can be tested against yours.
So which should you use for passwords?
OWASP's current order of preference:
- Argon2id, with at least 19 MiB of memory, 2 iterations and 1 degree of parallelism. Argon2id is memory-hard, which makes it much more expensive to attack with GPUs and custom hardware.
- scrypt, if Argon2id isn't available, with a cost parameter of at least 217, a block size of 8 and parallelism of 1.
- bcrypt, for legacy systems where neither of those is available, with a work factor of at least 10, set as high as your server can comfortably handle.
- PBKDF2, when FIPS-140 compliance is required, with 600,000 iterations or more of HMAC-SHA-256.
In practice, bcrypt is still everywhere and it isn't broken. If your framework already uses it with a reasonable cost, you're in decent shape. For a new system, choose Argon2id if your platform supports it. PHP, for example, offers it through password_hash() with PASSWORD_ARGON2ID when PHP is built with Argon2 support.
Whichever algorithm you pick, use your language's standard password hashing API rather than assembling salts and rounds yourself. Those APIs handle salt generation, encoding and constant-time comparison for you.
Choosing a bcrypt cost factor
The right cost depends on your hardware. A common target is a hash that takes a noticeable fraction of a second on your login server: slow enough to hurt attackers, fast enough that users and your CPU don't mind. The Bcrypt Generator times a test hash on your device and shows an estimate for each cost, which is a quick way to see how steeply the time grows. Because each step doubles the work, going from 10 to 12 makes every hash four times slower, for you and for an attacker.
When you raise the cost later, existing hashes keep working. Most libraries can tell you when a stored hash uses an older cost, so you can rehash the password the next time that user signs in.
Where SHA-256 belongs
SHA-256 is the right default for almost every non-password hashing job:
- Verifying downloads. Compare the SHA-256 published by the vendor with the one you compute locally.
- Signing and certificates. TLS certificates and code signing use SHA-256 or stronger.
- Webhooks and API signatures. Many services sign payloads with HMAC-SHA-256 so you can confirm the sender. HMAC combines the hash with a secret key, which is a different construction from hashing alone.
- Content addressing. Git has added a SHA-256 repository format as a successor to SHA-1, and many caching and storage systems name content by its SHA-256 digest.
FAQ
Can an MD5 or SHA-256 hash be decrypted?
No. Hashes are one-way, so there's nothing to decrypt. "MD5 decrypters" are lookup tables of hashes for known inputs. They work on common passwords precisely because unsalted MD5 always gives the same output.
Is salted SHA-256 good enough for passwords?
Salting fixes the lookup-table problem but not the speed problem. Each guess is still cheap, so OWASP recommends a dedicated password hashing function instead.
Is bcrypt outdated?
Not broken, but no longer the first choice. OWASP now lists it for legacy systems, after Argon2id and scrypt, mainly because it isn't memory-hard.
Does SHA-512 make passwords safer than SHA-256?
Not meaningfully. Both are fast general-purpose hashes. The fix for password storage is a slow, salted algorithm, not a longer digest.