A hash is a fixed-length fingerprint of data. Here is how MD5 and SHA-256 differ, why fast hashes are wrong for passwords, and what HMAC adds.
Password entropy measures how hard a secret is to guess, in bits. Here is the formula, why Password1! fails it, and how to generate something strong.
9 min read
A JWT is a signed bundle of JSON claims. Here is how to read one, why decoding is not verifying, and how to check for the classic JWT security flaws.
9 min read
A hash function turns any input into a short, fixed-length fingerprint called a digest. The same input always gives the same digest. Change one character and the digest changes completely. And you can't run it backwards to get the input. Hashes sit behind download checksums, password storage, Git, TLS certificates and API signatures, which is why you keep running into them.
Have a hash in front of you? The Hash Generator makes MD5, SHA-1, SHA-256 and SHA-512 digests. The Hash Identifier suggests which format a hash is. The HMAC Generator signs a message with a key. Use test data only.
The outputs below were captured on 12 October 2026 (IST) on a Linux box with GNU coreutils 9.7 and Python 3.13.5.
Here's the string hello world (no trailing newline) through four algorithms:
printf 'hello world' | md5sum
printf 'hello world' | sha1sum
printf 'hello world' | sha256sum
printf 'hello world' | sha512sum
5eb63bbbe01eeed093cb22bb8f5acdc3 -
2aae6c35c94fcfb415dbe95f408b9ce91ee846ed -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 -
309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f -
Eleven bytes in, and each algorithm gives the same length out no matter how big the input is. A 4 GB ISO gets a 64-character SHA-256 digest too.
The avalanche effect. Change the last letter:
printf 'hello world' | sha256sum
printf 'hello worle' | sha256sum
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 -
0fc30e735a0228a31cbbb969988b4f50e02e737f979f091d7d224b765443f5d4 -
Counting the bits that differ between the two digests:
python3 -c "
import hashlib
a = hashlib.sha256(b'hello world').digest()
b = hashlib.sha256(b'hello worle').digest()
bits = sum(bin(x ^ y).count('1') for x, y in zip(a, b))
print(bits, 'of 256 bits differ')
"
140 of 256 bits differ
Roughly half (on average it's 128), which is what you want: nothing about the new digest hints at how close the inputs were. FIPS 180-4 puts it as "any change to a message will, with a very high probability, result in a different message digest."
Three properties make a hash cryptographic. NIST SP 800-107 defines them:
| Reversible? | Key? | Purpose | |
|---|---|---|---|
| Encoding (Base64, hex) | Yes, by anyone | No | Represent data safely in another format |
| Encryption (AES, RSA) | Yes, with the key | Yes | Keep data secret |
| Hashing (SHA-256) | No | No | Fingerprint data, check integrity |
| HMAC (HMAC-SHA256) | No | Yes | Prove integrity and who sent it |
Base64 is not protection. Anyone can decode it with the Base64 tool. And you can't "decrypt" a hash, because nothing was encrypted.
| Algorithm | Digest | Hex chars | Spec | Status |
|---|---|---|---|---|
| MD5 | 128 bits | 32 | RFC 1321 | Broken for collisions |
| SHA-1 | 160 bits | 40 | FIPS 180-4 | Broken for collisions |
| SHA-256 | 256 bits | 64 | FIPS 180-4 | Current |
| SHA-512 | 512 bits | 128 | FIPS 180-4 | Current |
MD5. RFC 6151 says "MD5 is no longer acceptable where collision resistance is required such as digital signatures." Same RFC: it's "not urgent" to stop HMAC-MD5 in existing uses, but new designs shouldn't use it.
SHA-1. The SHAttered paper (2017) produced the first known SHA-1 collision: two different PDFs with the same SHA-1 hash. Its authors put the effort at about 6,500 CPU years and 100 GPU years. NIST SP 800-131A Rev. 2 disallows SHA-1 for generating digital signatures, except where protocol-specific guidance allows it.
SHA-2 and SHA-3. SHA-256 and SHA-512 (the SHA-2 family) are the current defaults. FIPS 202 adds SHA-3 (SHA3-256 and friends) as an alternative built on a different design, not a replacement because SHA-2 failed.
"Broken" here means collisions. A broken collision property matters when an attacker can pick the inputs, as with signatures and certificates. For an accidental-corruption check on a file you control, MD5 still notices the corruption. Still, there's rarely a reason to pick it for anything new.
Length and character set narrow it down:
Password hashes usually carry a prefix that names the scheme: $2b$ is bcrypt, $argon2id$ is Argon2id, $6$ is SHA-512-crypt and $y$ is yescrypt. Those last two are what you'll see in /etc/shadow. The Linux user and group management tutorial walks through those formats. For a quick guess, paste a test hash into the Hash Identifier; treat the answer as a list of candidates, not proof.
Projects publish a SHA-256 next to each download so you can confirm you got the exact bytes. Here's the check, then the same file with one byte added:
printf 'hello world' > demo.txt
sha256sum demo.txt > demo.txt.sha256
sha256sum -c demo.txt.sha256
printf '!' >> demo.txt
sha256sum -c demo.txt.sha256
demo.txt: OK
demo.txt: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match
On Windows, Get-FileHash in PowerShell does the same (SHA-256 by default). One catch: a checksum only helps if it comes from a channel the attacker can't also change. If someone swaps the file and the checksum on the same compromised mirror, both match. Signed checksum files or an HTTPS page from the project fix that. A hash answers "is this the exact file?". Magic bytes answer "what type of file is this?", which is a different question.
SHA-256 is designed to be fast. That's great for checksums and bad for passwords. If a database leaks, the attacker guesses passwords offline, and every guess costs one fast hash. The OWASP Password Storage Cheat Sheet says it directly: "Fast hashing algorithms such as SHA‑256 are not suitable for password storage because they allow attackers to perform large numbers of guesses quickly."
Salts. A salt is a unique random value stored next to each password hash. With salts, two users with the same password get different hashes. A rainbow table is a big precomputed lookup from hashes back to common passwords, and it's useless against salted hashes, because the attacker would need a separate table for every salt.
Pepper. A pepper is a secret shared across all passwords and kept outside the database, for example in a secrets manager. OWASP treats it as an extra layer, not a replacement for the salt.
What to use. OWASP's current recommendations, quoted:
These algorithms are slow by design and generate and store the salt for you. Use a maintained library, not your own loop around SHA-256. If you test password storage on an engagement, the pentest tools guide covers the cracking tools themselves; this piece stays on the defending side.
A plain hash proves nothing about who made it. Anyone can recompute sha256(message). HMAC (RFC 2104, FIPS 198-1) mixes a secret key into the hash. Only someone with the key can produce a valid tag. That's how webhook signatures and many API request-signing schemes work. It's also what the HS256 algorithm in a JWT is: HMAC with SHA-256.
Here's Python's hmac module with an obviously fake key:
python3 -c "
import hmac, hashlib
key = b'TEST-ONLY-fake-key-not-a-secret'
msg = b'{\"event\":\"ping\",\"id\":42}'
sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
print(sig)
print(hmac.compare_digest(sig, hmac.new(key, msg, hashlib.sha256).hexdigest()))
print(hmac.compare_digest(sig, hmac.new(b'wrong-key', msg, hashlib.sha256).hexdigest()))
"
c71e21bf5d4ec4e4fdc1a786fe319635934d521a1f1534dd0d69474cf5c3bb30
True
False
Two details matter when you verify a signature:
hmac.compare_digest, not ==. The Python docs say it avoids "content-based short circuiting behaviour", so response timing doesn't leak how many leading characters matched.Why not just sha256(secret + message)? SHA-256, like MD5 and SHA-1, is built so that, given a digest, someone can keep hashing extra data onto the end without knowing what came before. That's the length-extension problem. With a naive sha256(secret + message), an attacker who sees one tag may be able to forge a valid tag for the message plus appended data, without the secret. HMAC's nested construction was designed to avoid this. FIPS 202 notes SHA-3 was designed to resist length extension too. Either way, use HMAC rather than inventing your own.
Try the same thing with test data in the HMAC Generator.
No. A hash isn't encryption, so there's no key and nothing to decrypt. What people call "cracking" is guessing inputs and comparing hashes, which is why weak passwords and fast hashes are a bad mix.
Not where an attacker could choose the input: signatures, certificates, deduplication of untrusted files, passwords. For a quick check that a file you control wasn't corrupted in transit, it still detects accidents, but SHA-256 does that job just as well.
Not on its own. It's too fast. Use Argon2id, scrypt, bcrypt or PBKDF2 with the OWASP parameters above.
A salt is unique per password and stored with the hash. A pepper is one secret shared across all passwords and stored separately, so a database leak alone doesn't reveal it.
Only test data. Never paste real passwords, API keys or customer data into any web tool. A hash of a weak password is as good as the password to someone guessing.
An IPv4 address is one 32-bit number. Here is how to read it as decimal, hex, binary or integer, size a CIDR block, and spot private ranges.
9 min read