Choosing a Hash Function: MD5, SHA-256, and What to Use for Passwords
A hash function maps input of any length to a fixed-length value, one way only. The same input always produces the same output, the output cannot be reversed, and flipping a single input bit changes the output completely. But 'we use hashing' guarantees nothing on its own — picking the right function for the job is the whole task.
Algorithm comparison
Saying MD5 and SHA-1 are 'broken' means an attacker can construct two different inputs with the same hash — a collision. They can no longer prove a file hasn't been tampered with. In settings with no adversary, though, such as generating cache keys or distributing data across buckets, MD5 remains fast and perfectly practical.
| Algorithm | Output size | Status | Recommended for |
|---|---|---|---|
| MD5 | 128-bit (32 hex) | Practical collisions since 2004 | Non-security uses only: checksums, cache keys |
| SHA-1 | 160-bit (40 hex) | Collision demonstrated (SHAttered, 2017) | Do not use in new designs |
| SHA-256 | 256-bit (64 hex) | Secure | Integrity checks, signatures — the default choice |
| SHA-512 | 512-bit (128 hex) | Secure | Can outperform SHA-256 on 64-bit hardware |
| bcrypt / scrypt / Argon2 | Variable | Secure | Password storage (see below) |
How to use it
- Enter the text you want to hash.
- Read the result for the algorithm you need — several algorithms are shown for the same input so you can compare.
- To check file integrity, compare against the value published by the distributor. Normalize both to lowercase before comparing.
Why SHA-256 is wrong for passwords
What makes SHA-256 good elsewhere makes it bad here. The SHA family is deliberately very fast, and a modern GPU computes billions of SHA-256 operations per second — so a leaked table of hashes can be attacked by dictionary and brute force at enormous speed.
Passwords need a deliberately slow hash. bcrypt, scrypt, and Argon2 expose the computational cost as a tunable parameter (and scrypt and Argon2 add memory cost), specifically to throttle an attacker's attempts. For new systems Argon2id is the current recommendation, and bcrypt remains a well-proven choice.
Salting is mandatory too. A salt is a per-user random value mixed into the password so identical passwords produce different hashes. That defeats rainbow tables and also stops anyone reading a leaked database from spotting which users share a password. bcrypt and Argon2 generate a salt automatically and embed it in the output string, so there's nothing extra to manage.
import bcrypt from "bcrypt"; // storing — cost factor 12 is a reasonable default; tune to your hardware const hash = await bcrypt.hash(password, 12); // yields $2b$12$Ku0z... with the salt embedded // checking — use compare, never string equality const ok = await bcrypt.compare(password, hash);
Hashes versus HMACs
When you need to establish that data came from someone holding a specific secret — verifying a webhook, for instance — use an HMAC rather than a plain hash. Naive constructions like SHA-256(secret + data) are vulnerable to length-extension attacks; HMAC is designed to avoid that structural flaw. GitHub, Stripe, and Slack all sign webhooks with HMAC-SHA256.
When comparing signature values, use a constant-time comparison (Node's crypto.timingSafeEqual, for example) rather than == or ===. Ordinary string comparison returns at the first differing byte, which lets an attacker recover a valid signature one byte at a time by measuring response times.
Common uses
- Comparing a downloaded installer's SHA-256 checksum against the publisher's value
- Understanding how Git identifies objects (Git uses SHA-1 plus added collision detection)
- Generating cache keys or ETags so the key changes when content changes
- Detecting duplicate files by comparing content quickly
- Distributing keys evenly when sharding a database
- Comparing identity without storing personal data — though low-entropy values like email addresses aren't really protected by hashing alone
Frequently Asked Questions
- Can a hash be decrypted?
- No — hashing isn't encryption but a one-way function, mathematically irreversible. The 'hash decryption' sites you'll find are really looking your value up in a huge precomputed table of (input, hash) pairs. That's why common passwords resolve instantly and high-entropy values never do, and it's exactly what salting defeats.
- Is MD5 completely useless now?
- It must not be used for security, but it's still useful where nobody can benefit from forging a collision: cache keys, local change detection, bucket selection for data distribution. The test is whether a deliberately crafted collision would cause harm. If it would, use SHA-256.
- Does a longer output mean more security?
- A longer output raises the work needed for brute force and collision search, so in that dimension yes. But length doesn't patch structural weaknesses in the algorithm: SHA-1 is 160 bits and unsafe, while SHA-256 is 256 bits and currently sufficient. And for password storage the key property isn't length at all — it's slowness.
- The same text gives me different hashes.
- The input almost certainly differs. Usual culprits are a trailing newline, leading or trailing whitespace, and line-ending differences (CRLF on Windows versus LF on Unix). For files, opening in text mode can rewrite line endings. Differing character encodings — UTF-8 versus a legacy codepage — change the bytes and therefore the hash.
- Is my input sent to a server?
- No, it's computed locally through the browser's Web Crypto API. That said, entering a real password here and storing the result is the wrong approach in the first place — passwords should be processed server-side with bcrypt or Argon2.
- Are hash values case-sensitive?
- The hash itself is bytes; rendering it as hex in uppercase or lowercase represents the same value. Code that compares them as strings will report a mismatch, however, so normalize both sides to lowercase before comparing.
💡 Note: Eyeballing the first and last few characters of a checksum isn't a real check. Copy and compare the full string, or use a verification command like shasum -c.