When to use SHA-256 vs HMAC vs bcrypt
SHA-256, HMAC, and bcrypt show up in the same conversations, so people treat them as interchangeable knobs. They are not. Each solves a different problem, and using one where another belongs is a common source of real security holes. The short version: SHA-256 proves data has not changed, HMAC proves data has not changed and came from someone holding a shared key, and bcrypt stores passwords so that a database leak does not hand attackers every account. Here is how to tell them apart and pick correctly.
SHA-256: integrity and fingerprints
SHA-256 is a cryptographic hash function. You feed it any input and it returns a fixed 256 bit value. The same input always produces the same output, and any change to the input, even one bit, produces a completely different output. It takes no key.
What that buys you:
- Integrity checks. Publish a file alongside its SHA-256 digest. Anyone who downloads the file can hash it and confirm the digest matches, proving the bytes were not altered or corrupted in transit.
- Fingerprints and deduplication. Use the digest as a stable identifier for a blob of content. If two files share a digest, they are the same file for practical purposes.
- Commitments. Store a hash of a value now and reveal the value later, proving you did not change it.
What SHA-256 does not do: it does not prove who produced the data. Because there is no key, anyone can recompute a valid digest after tampering with the content. If an attacker can change the file, they can change the published digest too. SHA-256 protects against accidental corruption and lets you detect changes, but on its own it is not an authentication mechanism.
It is also fast, by design. A modern machine computes billions of SHA-256 hashes per second. That speed is exactly why it is the wrong tool for passwords, which we will get to.
You can compute SHA-256, SHA-1, SHA-512, and MD5 digests in your browser with the Hash Generator. Nothing you type is uploaded; the hashing runs locally on your machine.
HMAC: authenticated integrity with a shared key
HMAC (Hash based Message Authentication Code) wraps a hash function like SHA-256 with a secret key. The output is a tag that depends on both the message and the key. Anyone who holds the same key can verify the tag; anyone who does not cannot produce a valid one.
This closes the gap SHA-256 leaves open. HMAC proves two things at once:
- The message was not modified (integrity).
- The message came from someone who knows the key (authenticity).
Where you actually use it:
- Webhook verification. Stripe, GitHub, and many providers sign each webhook payload with HMAC-SHA256 using a secret you both hold. Your endpoint recomputes the tag and rejects requests that do not match, so an attacker cannot forge events.
- API request signing. AWS Signature and similar schemes sign requests with HMAC so the server confirms the request came from the holder of the secret key and was not tampered with in flight.
- Stateless tokens. A signed token carries a tag the server can verify without a database lookup.
The critical rule: verify HMAC tags using a constant time comparison, not a plain equality check, to avoid leaking information through timing. And keep the key secret. The whole guarantee collapses if the key leaks.
If you are working with signed tokens, the JWT Decoder lets you inspect a token's header and claims locally, and the HMAC Generator computes tags for a given message and key so you can match what a provider sends.
bcrypt: storing passwords
Passwords need a third, very different property. When your user database leaks, and you should plan for the day it does, the attacker should not be able to recover the original passwords. That means you never store passwords, and you never store a plain SHA-256 of them either.
Here is why a fast hash fails. SHA-256 is built to be fast, so an attacker who steals your hashed passwords can try billions of guesses per second against each one. Common passwords fall in seconds. Worse, identical passwords produce identical hashes, so attackers precompute rainbow tables and reverse millions of accounts at once.
bcrypt, scrypt, and Argon2 are password hashing functions designed to defeat exactly this:
- They are deliberately slow and tunable. You set a cost factor that makes each hash take, say, a quarter second. That is invisible to a user logging in but devastating to an attacker running billions of guesses.
- They salt automatically. A random salt is generated per password and stored with the hash, so identical passwords produce different hashes and rainbow tables are useless.
- They resist hardware acceleration. scrypt and Argon2 are also memory hard, making GPU and ASIC attacks far more expensive.
For new systems, prefer Argon2id. bcrypt remains a solid, widely supported choice. Never roll your own with SHA-256 plus a salt and a loop; use a vetted library and let it manage the cost factor and salt. When you set password rules, lean on length and a check like the Password Strength Analyzer rather than arbitrary character requirements.
Quick decision guide
- Verifying a download, fingerprinting content, or detecting accidental change, with no secret involved: SHA-256.
- Confirming a message came from a party who shares a secret key, such as a webhook or signed API request: HMAC.
- Storing user passwords so a leak does not expose them: bcrypt, scrypt, or Argon2, never a fast hash.
The mistake to avoid is using a plain fast hash for passwords or treating a keyless hash as proof of origin. Match the tool to the guarantee you actually need, and these three stay clearly separate.