Cryptographic Hash Functions Explained - SHA-256, SHA-512 and More
Cryptographic Hash Functions Explained - SHA-256, SHA-512 and More
A comprehensive guide to cryptographic hash functions including SHA-256, SHA-512, MD5, and more. Learn about hash properties, use cases like checksums, password hashing, blockchain, and digital signatures.
Introduction: What Are Hash Functions?
A hash function is a mathematical function that takes an input of any size and produces a fixed-size output, called a hash, digest, or checksum. Cryptographic hash functions add security properties that make them invaluable for software development, cybersecurity, blockchain technology, and data integrity verification.
Hash functions are everywhere in modern computing. Every time you log into a website, verify a file download, commit code to Git, or interact with a blockchain, hash functions are working behind the scenes. Understanding how they work and when to use different hash algorithms is essential knowledge for every developer.
Want to generate hashes quickly? Try our Hash Generator tool, which supports MD5, SHA-1, SHA-256, SHA-384, and SHA-512.
Core Properties of Cryptographic Hash Functions
A good cryptographic hash function must have several key properties that distinguish it from a simple checksum or ordinary hash function.
1. Deterministic
The same input always produces the same output. If you hash the string "Hello, World!" with SHA-256, you will get the same hash every single time, on every machine, in every programming language.
Input: "Hello, World!"
SHA-256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
2. Fixed Output Size
Regardless of input size, the output is always the same length:
| Algorithm | Output Size (bits) | Output Size (hex chars) |
|---|---|---|
| MD5 | 128 | 32 |
| SHA-1 | 160 | 40 |
| SHA-224 | 224 | 56 |
| SHA-256 | 256 | 64 |
| SHA-384 | 384 | 96 |
| SHA-512 | 512 | 128 |
| SHA-3-256 | 256 | 64 |
| BLAKE3 | 256 (default) | 64 |
Input: "a"
SHA-256: ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
Input: "The quick brown fox jumps over the lazy dog"
SHA-256: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
Input: [entire contents of a 1GB file]
SHA-256: [still exactly 64 hex characters]
3. Pre-Image Resistance
Given a hash value, it should be computationally infeasible to find an input that produces that hash. In other words, hash functions are one-way functions -- you cannot reverse them.
Hash: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Can you find the input? Only by brute force.
(It's the empty string, by the way)
4. Second Pre-Image Resistance
Given an input and its hash, it should be computationally infeasible to find a different input that produces the same hash.
5. Collision Resistance
It should be computationally infeasible to find any two different inputs that produce the same hash output.
6. Avalanche Effect
A small change in the input should cause a dramatic change in the output. Changing even a single bit should change roughly half of the output bits.
Input: "Hello, World!"
SHA-256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
Input: "Hello, World?" (changed ! to ?)
SHA-256: 4a6e2ef4e4fa0e534f1d1989f453daa87f36e5e4e35b3dcb3d82f22d12654083
These two hashes look completely different despite the inputs differing by a single character. You can verify this yourself using our Hash Generator.
The SHA Family of Hash Functions
The Secure Hash Algorithm (SHA) family is the most widely used set of cryptographic hash functions. It was developed by the National Security Agency (NSA) and published by NIST.
SHA-1 (Deprecated)
SHA-1 produces a 160-bit (20-byte) hash. It was the standard for many years but is now considered broken and should not be used for security purposes.
- Status: Deprecated -- do not use for new applications
- Collision found: Google and CWI Amsterdam demonstrated a practical collision in 2017 (SHAttered attack)
- Still used: Git still uses SHA-1 for object IDs (transitioning to SHA-256)
- Output: 40 hexadecimal characters
SHA-256
SHA-256 is the most widely used hash function today. It is part of the SHA-2 family and produces a 256-bit (32-byte) hash.
- Status: Secure and widely recommended
- Used in: Bitcoin, TLS certificates, code signing, file integrity checks
- Performance: Moderate -- fast enough for most applications
- Output: 64 hexadecimal characters
// JavaScript: Generating SHA-256 hash
async function sha256(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
const hash = await sha256('Hello, World!');
console.log(hash);
// dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
SHA-384
SHA-384 is a truncated version of SHA-512 that produces a 384-bit hash. It offers slightly better security margins than SHA-256.
- Status: Secure
- Used in: TLS, high-security applications
- Output: 96 hexadecimal characters
SHA-512
SHA-512 produces a 512-bit (64-byte) hash. It is actually faster than SHA-256 on 64-bit systems because it processes data in larger blocks.
- Status: Secure
- Performance: Often faster than SHA-256 on 64-bit systems
- Used in: Password hashing, digital signatures, high-security applications
- Output: 128 hexadecimal characters
import hashlib
# SHA-512 in Python
message = "Hello, World!"
hash_object = hashlib.sha512(message.encode('utf-8'))
hex_digest = hash_object.hexdigest()
print(hex_digest)
# 374d794a95cdcfd8b35993185fef9ba368f160d8daf432d08ba9f1ed1e5abe6cc69291e0fa2fe0006a52570ef18c19def4e617c33ce52ef0a6e5fbe318cb0387
SHA-2 Family Comparison
| Algorithm | Output Bits | Block Size | Internal State | Security Level |
|---|---|---|---|---|
| SHA-224 | 224 | 512 | 256 | 112 bits |
| SHA-256 | 256 | 512 | 256 | 128 bits |
| SHA-384 | 384 | 1024 | 512 | 192 bits |
| SHA-512 | 512 | 1024 | 512 | 256 bits |
| SHA-512/224 | 224 | 1024 | 512 | 112 bits |
| SHA-512/256 | 256 | 1024 | 512 | 128 bits |
SHA-3 (Keccak)
SHA-3 is the newest member of the SHA family, based on the Keccak algorithm. It uses a fundamentally different construction (sponge construction) from SHA-2, providing algorithm diversity.
- Status: Secure, standardized in 2015
- Construction: Sponge construction (different from SHA-2's Merkle-Damgard)
- Variants: SHA-3-224, SHA-3-256, SHA-3-384, SHA-3-512, SHAKE128, SHAKE256
- Use case: Backup option if SHA-2 is ever compromised
MD5: Why It's Still Around
MD5 produces a 128-bit hash and was once the most popular hash function. It is now cryptographically broken but still used for non-security purposes.
Do NOT use MD5 for:
- Password hashing
- Digital signatures
- Certificate verification
- Any security-critical application
MD5 is still acceptable for:
- Non-cryptographic checksums (verifying file integrity against accidental corruption)
- Content-addressable storage (where security is not a concern)
- Deduplication
- Legacy system compatibility
import hashlib
# MD5 is still commonly seen in file checksums
md5_hash = hashlib.md5(b"Hello, World!").hexdigest()
print(md5_hash) # 65a8e27d8879283831b664bd8b7f0ad4
BLAKE3: The Modern Alternative
BLAKE3 is a newer hash function that offers significant performance improvements over SHA-2 and SHA-3 while maintaining strong security properties.
BLAKE3 advantages:
- Extremely fast: 4-8x faster than SHA-256 on modern CPUs
- Parallelizable: Can take advantage of SIMD instructions and multiple cores
- Secure: Based on the well-analyzed BLAKE2 and ChaCha stream cipher
- Versatile: Can function as a hash, MAC, KDF, and XOF
use blake3;
fn main() {
let hash = blake3::hash(b"Hello, World!");
println!("{}", hash.to_hex());
// 288a26b2bfb0602c0c7c9e4bf714f53f46c090da7e7ab8a30af9bf6c8e3bf0f8
}
Use Cases for Hash Functions
1. File Integrity and Checksums
Hash functions verify that files have not been corrupted or tampered with during transfer.
# Generate checksum
sha256sum ubuntu-24.04.iso > checksum.txt
# Verify checksum
sha256sum -c checksum.txt
# ubuntu-24.04.iso: OK
// Verify file integrity in JavaScript
async function verifyFileIntegrity(file, expectedHash) {
const buffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const actualHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return actualHash === expectedHash;
}
Use our Hash Generator to quickly compute checksums for your files and strings.
2. Password Hashing
Storing passwords in plain text is a critical security vulnerability. Hash functions allow you to verify passwords without storing them.
Important: Do NOT use plain SHA-256 for password hashing. Use a dedicated password hashing function that includes salting and is intentionally slow.
Recommended password hashing algorithms:
| Algorithm | Status | Key Feature |
|---|---|---|
| Argon2id | Best choice | Memory-hard, GPU-resistant |
| bcrypt | Proven | Widely supported, battle-tested |
| scrypt | Good | Memory-hard |
| PBKDF2 | Acceptable | NIST-approved, widely available |
// Node.js password hashing with bcrypt
import bcrypt from 'bcrypt';
// Hashing a password
const saltRounds = 12;
const passwordHash = await bcrypt.hash('user_password', saltRounds);
// Result: $2b$12$LJ3m6gEwO/fSFqCVXWLwOeR/dYtTVkRDCwoGLBE0Fg6voFEOB5viy
// Verifying a password
const isValid = await bcrypt.compare('user_password', passwordHash);
// true
// Argon2id (preferred for new applications)
import argon2 from 'argon2';
const hash = await argon2.hash('user_password', {
type: argon2.argon2id,
memoryCost: 65536, // 64MB
timeCost: 3,
parallelism: 4,
});
const valid = await argon2.verify(hash, 'user_password');
Why plain hashing is not enough:
// Bad: Plain SHA-256 hash of "password123"
ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f
// An attacker with a rainbow table can reverse this instantly.
// Solution: Use a proper password hashing function with salt.
For more on password security, check our Password Security Guide and generate strong passwords with our Password Generator.
3. Blockchain and Cryptocurrency
Hash functions are fundamental to blockchain technology. Bitcoin uses SHA-256 in its proof-of-work consensus mechanism.
import hashlib
import time
def mine_block(data, difficulty):
"""Simple proof-of-work mining simulation"""
prefix = '0' * difficulty
nonce = 0
while True:
text = f'{data}{nonce}'
hash_result = hashlib.sha256(text.encode()).hexdigest()
if hash_result.startswith(prefix):
return nonce, hash_result
nonce += 1
# Mine a block with difficulty 4 (hash must start with 0000)
nonce, hash_value = mine_block("Block data here", 4)
print(f"Nonce: {nonce}")
print(f"Hash: {hash_value}")
# Hash will start with "0000..."
Hash functions in blockchain:
- Block hashing: Each block contains the hash of the previous block, creating an immutable chain
- Merkle trees: Transactions are organized in a tree structure where each node is a hash of its children
- Mining: Proof-of-work requires finding a nonce that produces a hash below a target value
- Addresses: Cryptocurrency addresses are derived from public key hashes
4. Digital Signatures
Digital signatures use hash functions to sign messages efficiently. Instead of signing the entire message (which could be very large), the signer hashes the message and signs the hash.
// Simplified digital signature flow
// 1. Hash the message
const messageHash = await sha256(message);
// 2. Sign the hash with the private key
const signature = await crypto.subtle.sign(
{ name: 'RSASSA-PKCS1-v1_5' },
privateKey,
encoder.encode(messageHash)
);
// 3. Verify: hash the message and check the signature
const verifyHash = await sha256(message);
const isValid = await crypto.subtle.verify(
{ name: 'RSASSA-PKCS1-v1_5' },
publicKey,
signature,
encoder.encode(verifyHash)
);
5. Content-Addressable Storage
Systems like Git, IPFS, and Docker use hash functions to address content by its hash. This provides deduplication, integrity verification, and immutability.
# Git uses SHA-1 (transitioning to SHA-256) for object IDs
$ echo "Hello, World!" | git hash-object --stdin
8ab686eafeb1f44702738c8b0f24f2567c36da6d
# Docker layer content addressing
$ docker inspect --format='{{.Id}}' my-image
sha256:abc123...
6. HMAC (Hash-Based Message Authentication Code)
HMAC combines a hash function with a secret key to provide both data integrity and authentication.
// Node.js HMAC
import { createHmac } from 'crypto';
function generateHmac(message, secretKey) {
return createHmac('sha256', secretKey)
.update(message)
.digest('hex');
}
// Webhook signature verification
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = generateHmac(payload, secret);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// API request signing
const timestamp = Date.now().toString();
const body = JSON.stringify({ action: 'transfer', amount: 100 });
const signaturePayload = `${timestamp}.${body}`;
const signature = generateHmac(signaturePayload, API_SECRET);
fetch('https://api.example.com/transactions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Timestamp': timestamp,
'X-Signature': signature,
},
body: body,
});
7. Subresource Integrity (SRI)
Browsers can verify that fetched resources (scripts, stylesheets) have not been tampered with using SRI hashes.
{/* SRI: Browser verifies the hash before executing the script */}
<script
src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdn.example.com/styles.css"
integrity="sha256-5IxE/yBJ7PB5TbXxYqBCj9n5xM4mECft3p7/+Gkh1I="
crossorigin="anonymous"
/>
# Generate SRI hash
echo -n "file contents" | openssl dgst -sha384 -binary | openssl base64 -A
# Or use: shasum -b -a 384 file.js | xxd -r -p | base64
8. Data Deduplication
Hash functions identify duplicate data blocks in storage systems:
// Simple deduplication using content hashing
class DeduplicatedStorage {
constructor() {
this.store = new Map(); // hash -> data
this.references = new Map(); // hash -> reference count
}
async put(data) {
const hash = await sha256(data);
if (!this.store.has(hash)) {
this.store.set(hash, data);
this.references.set(hash, 1);
} else {
this.references.set(hash, this.references.get(hash) + 1);
}
return hash;
}
get(hash) {
return this.store.get(hash);
}
}
Hash Function Security Considerations
Known Vulnerabilities
| Algorithm | Status | Known Attacks |
|---|---|---|
| MD5 | Broken | Collision attacks, practical forgery |
| SHA-1 | Broken | SHAttered collision attack (2017) |
| SHA-256 | Secure | No known practical attacks |
| SHA-512 | Secure | No known practical attacks |
| SHA-3 | Secure | No known practical attacks |
| BLAKE3 | Secure | No known practical attacks |
Choosing the Right Hash Function
For general-purpose hashing (checksums, data integrity):
- Use SHA-256 for security-critical applications
- Use BLAKE3 for performance-critical applications
- MD5 is acceptable for non-security checksums
For password hashing:
- Use Argon2id (best)
- Use bcrypt (well-proven)
- Never use plain SHA-256/SHA-512
For digital signatures:
- Use SHA-256 or SHA-384
- Use SHA-512 for long-term security
For blockchain:
- SHA-256 (Bitcoin, most chains)
- Keccak-256 (Ethereum)
For HMAC:
- HMAC-SHA-256 (standard)
- HMAC-SHA-512 (higher security margin)
Length Extension Attacks
SHA-256 and SHA-512 (but not SHA-3 or BLAKE3) are vulnerable to length extension attacks. This means that given hash(message), an attacker can compute hash(message || padding || extension) without knowing the original message.
Mitigation: Use HMAC instead of raw hash functions for authentication. HMAC is not vulnerable to length extension attacks.
// Vulnerable to length extension attack
const token = sha256(secret + message);
// Safe: Use HMAC
const token = hmacSha256(secret, message);
Implementing Hash Functions
JavaScript (Browser and Node.js)
// Browser Web Crypto API
async function hash(algorithm, data) {
const encoder = new TextEncoder();
const encoded = encoder.encode(data);
const hashBuffer = await crypto.subtle.digest(algorithm, encoded);
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
// Usage
await hash('SHA-256', 'Hello, World!');
await hash('SHA-384', 'Hello, World!');
await hash('SHA-512', 'Hello, World!');
// Node.js crypto module
import { createHash } from 'crypto';
function hashNode(algorithm, data) {
return createHash(algorithm).update(data).digest('hex');
}
hashNode('sha256', 'Hello, World!');
hashNode('sha512', 'Hello, World!');
hashNode('md5', 'Hello, World!');
Python
import hashlib
# All major algorithms
data = b"Hello, World!"
print("MD5: ", hashlib.md5(data).hexdigest())
print("SHA-1: ", hashlib.sha1(data).hexdigest())
print("SHA-256:", hashlib.sha256(data).hexdigest())
print("SHA-384:", hashlib.sha384(data).hexdigest())
print("SHA-512:", hashlib.sha512(data).hexdigest())
print("SHA3-256:", hashlib.sha3_256(data).hexdigest())
# File hashing
def hash_file(filepath, algorithm='sha256'):
h = hashlib.new(algorithm)
with open(filepath, 'rb') as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
Go
package main
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
)
func main() {
data := []byte("Hello, World!")
// SHA-256
sha256Hash := sha256.Sum256(data)
fmt.Printf("SHA-256: %x\n", sha256Hash)
// SHA-512
sha512Hash := sha512.Sum512(data)
fmt.Printf("SHA-512: %x\n", sha512Hash)
}
Performance Comparison
Performance varies significantly between hash algorithms. Here are approximate benchmarks on modern hardware:
| Algorithm | Speed (MB/s) | Relative Speed |
|---|---|---|
| MD5 | ~700 | 1.4x |
| SHA-1 | ~550 | 1.1x |
| SHA-256 | ~500 | 1.0x (baseline) |
| SHA-512 | ~600 | 1.2x (on 64-bit) |
| SHA-3-256 | ~400 | 0.8x |
| BLAKE3 | ~3000+ | 6.0x+ |
Note: BLAKE3's performance advantage comes from its ability to leverage SIMD instructions and parallelism. Actual performance depends on hardware and implementation.
Conclusion
Cryptographic hash functions are fundamental building blocks of modern computing and security. Understanding the properties, strengths, and appropriate use cases of different hash algorithms is essential for any developer working with security, data integrity, or distributed systems.
Key takeaways:
- SHA-256 is the safe default for most cryptographic hashing needs
- Never use MD5 or SHA-1 for security purposes
- Use dedicated password hashing (Argon2id, bcrypt) -- never plain hash functions
- HMAC protects against length extension attacks -- use it for authentication
- BLAKE3 is the performance champion when you need speed
- The avalanche effect is your friend -- small input changes produce entirely different hashes
- Collision resistance matters -- choose algorithms with no known practical collisions
For quick hash generation and comparison, bookmark our Hash Generator tool. It supports MD5, SHA-1, SHA-256, SHA-384, and SHA-512, making it easy to compute and verify hashes directly in your browser.
Related Resources
- Hash Generator Tool -- Generate hashes instantly
- Password Security Guide -- Secure password practices
- Password Generator -- Generate strong passwords
- API Security Best Practices -- Hashing in API security
- Base64 Encoding Explained -- Encoding vs. hashing