How to Generate Hashes (MD5, SHA-256, SHA-512)
Learn how hash functions work and how to generate MD5, SHA-256, and SHA-512 hashes in code. Includes security best practices for file integrity and password storage.
A hash function takes any input (text, files, anything) and produces a fixed-length string of characters called a digest. The same input always produces the same output, but even a tiny change in the input produces a completely different hash.
This property makes hashes perfect for verifying file integrity, detecting duplicates, and (when done correctly) storing passwords.
The catch: not all hash algorithms are created equal. MD5 and SHA-1 are broken for security purposes. If you’re building anything security-sensitive, you need SHA-256 or stronger.
How to Generate Hashes in Code
Here’s how to generate common hash types in popular languages.
JavaScript (Node.js)
Node’s built-in crypto module handles all major hash algorithms.
const crypto = require('crypto');
// MD5 (NOT secure - use for checksums only)
const md5 = crypto.createHash('md5').update('hello').digest('hex');
console.log(md5); // "5d41402abc4b2a76b9719d911017c592"
// SHA-256 (secure)
const sha256 = crypto.createHash('sha256').update('hello').digest('hex');
console.log(sha256); // "2cf24dba5fb0a30e..."
// SHA-512 (even stronger)
const sha512 = crypto.createHash('sha512').update('hello').digest('hex');
console.log(sha512); // "9b71d224bd62f378..."
JavaScript (Browser)
Modern browsers have built-in support for SHA-based hashing.
async function sha256(text) {
const buffer = new TextEncoder().encode(text);
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
const hash = await sha256('hello');
console.log(hash); // "2cf24dba5fb0a30e..."
Note: MD5 isn’t available in browser crypto. This is intentional - browsers don’t want to encourage insecure hashing.
Python
import hashlib
text = "hello"
# MD5
md5 = hashlib.md5(text.encode()).hexdigest()
print(md5) # "5d41402abc4b2a76b9719d911017c592"
# SHA-256
sha256 = hashlib.sha256(text.encode()).hexdigest()
print(sha256) # "2cf24dba5fb0a30e..."
# SHA-512
sha512 = hashlib.sha512(text.encode()).hexdigest()
print(sha512) # "9b71d224bd62f378..."
Bash (Command Line)
Linux and macOS have built-in hash utilities.
# MD5
echo -n "hello" | md5sum
# 5d41402abc4b2a76b9719d911017c592
# SHA-256
echo -n "hello" | sha256sum
# 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
# Hash a file
sha256sum file.zip
On macOS, use md5, shasum -a 256, or shasum -a 512 instead.
Hash Algorithm Comparison
Not all hashing algorithms serve the same purpose. Here’s what to use when.
| Algorithm | Output Length | Speed | Security Status | Best Use Case |
|---|---|---|---|---|
| MD5 | 128 bits (32 hex) | Very fast | BROKEN | File checksums (non-security) |
| SHA-1 | 160 bits (40 hex) | Fast | BROKEN | Legacy systems only |
| SHA-256 | 256 bits (64 hex) | Fast | Secure | File integrity, digital signatures |
| SHA-512 | 512 bits (128 hex) | Fast | Secure | High-security applications |
| bcrypt | Variable | Slow (intentional) | Secure | Password hashing only |
| Argon2 | Variable | Slow (intentional) | Secure | Modern password hashing |
MD5 and SHA-1 are cryptographically broken. Attackers can create collisions (different inputs that produce the same hash). Don’t use them for security.
SHA-256 and SHA-512 are secure for file integrity, digital signatures, and blockchain applications.
Never use fast hashes for passwords. Use bcrypt, scrypt, or Argon2 instead - they’re intentionally slow to resist brute-force attacks.
Anatomy of a Hash
A hash is a fixed-length hexadecimal string. Let’s break down a SHA-256 hash:
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Key Properties:
- Deterministic: The same input always produces the same hash.
- Fixed Length: SHA-256 always produces 64 hex characters (256 bits), regardless of input size.
- One-Way: You can’t reverse a hash to get the original input.
- Avalanche Effect: Changing one character in the input completely changes the output.
Example - Tiny Input Change:
Input: "hello"
SHA-256: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Input: "Hello" (capital H)
SHA-256: 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
Completely different hashes, even though we only changed one letter’s capitalization.
Use Cases for Hash Functions
File Integrity Verification
When you download software, the publisher often provides a hash checksum. You hash the downloaded file and compare it to the published hash to verify the file wasn’t corrupted or tampered with.
# Download file
curl -O https://example.com/software.zip
# Verify against published hash
sha256sum software.zip
# Compare output to the hash on the download page
If the hashes match, the file is authentic. If they differ, the file was modified (accidentally or maliciously).
Detecting Duplicate Files
Cloud storage services use hashes to deduplicate files. If two users upload the same file, the service stores it once and tracks two references to it.
const files = ['file1.txt', 'file2.txt', 'file3.txt'];
const hashes = new Map();
files.forEach(file => {
const content = fs.readFileSync(file);
const hash = crypto.createHash('sha256').update(content).digest('hex');
if (hashes.has(hash)) {
console.log(`${file} is a duplicate of ${hashes.get(hash)}`);
} else {
hashes.set(hash, file);
}
});
Content Addressing
Git uses SHA-1 hashes to uniquely identify every commit, tree, and blob. The hash of the content becomes its address.
Example: Git commit hash a3f7d9c is the SHA-1 hash of the commit’s metadata and changes.
Digital Signatures
Signing large files is slow. Instead, sign the hash of the file (which is fast), then verify the signature against the hash.
1. Hash the file → SHA-256 digest
2. Sign the digest with private key → Signature
3. Recipient hashes the file → SHA-256 digest
4. Verify signature against digest → Confirms authenticity
Password Storage (With Critical Caveats)
Storing passwords in plain text is suicide. Storing SHA-256 hashes of passwords is almost as bad.
Wrong Way:
// NEVER DO THIS
const passwordHash = crypto.createHash('sha256')
.update(password)
.digest('hex');
// Vulnerable to rainbow tables and brute force
Right Way:
const bcrypt = require('bcrypt');
// Hash with bcrypt (slow, salted, secure)
const hash = await bcrypt.hash(password, 10);
// Result includes salt and cost factor
Why? Fast hashes like SHA-256 can be brute-forced at billions of attempts per second on GPUs. Bcrypt/Argon2 are designed to be slow (taking ~100ms per attempt), making brute-force attacks impractical.
Security Considerations
MD5 and SHA-1 Are Broken for Security
In 2017, Google demonstrated a practical SHA-1 collision attack. Two different PDF files produced the same SHA-1 hash. This breaks digital signatures and certificate validation.
Verdict: Use SHA-256 or stronger for anything security-related.
Safe uses for MD5:
- Non-critical checksums (verifying file transfer integrity within trusted networks)
- Cache keys (where collisions cause bugs, not security breaches)
- Database row deduplication
Rainbow Tables and Salting
A rainbow table is a precomputed database of hashes for common passwords. If you store unsalted password hashes, attackers can reverse them instantly.
Example:
SHA-256("password123") = ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f
This hash appears in every rainbow table. An attacker with your database can crack millions of passwords in seconds.
Solution: Add a random salt before hashing.
const salt = crypto.randomBytes(16).toString('hex');
const hash = crypto.createHash('sha256')
.update(salt + password)
.digest('hex');
// Store both salt and hash
Better yet, use bcrypt/Argon2 which handle salting automatically.
Hash Length and Collision Probability
Longer hashes reduce collision probability. A collision is when two different inputs produce the same hash.
Birthday Paradox Math:
- MD5 (128 bits): 50% collision chance after 2^64 hashes (~18 quintillion)
- SHA-256 (256 bits): 50% collision chance after 2^128 hashes (functionally impossible)
For perspective, 2^128 is more than the number of atoms in the observable universe.
Common Mistakes to Avoid
Using Hashes for Encryption
Hashes are one-way functions. You can’t decrypt a hash to recover the original data.
Wrong:
// This makes no sense
const encrypted = crypto.createHash('sha256').update(secret).digest('hex');
const decrypted = ??? // Impossible
Right:
// Use encryption for reversible operations
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const encrypted = cipher.update(secret, 'utf8', 'hex') + cipher.final('hex');
// Can be decrypted with the key
Not Comparing Hashes Safely
Comparing hashes with == or === can leak timing information, allowing attackers to determine if they’re getting close to the correct hash.
Vulnerable:
if (userHash === storedHash) {
// Timing attack: comparison stops at first different character
}
Secure:
const crypto = require('crypto');
if (crypto.timingSafeEqual(Buffer.from(userHash), Buffer.from(storedHash))) {
// Constant-time comparison
}
Hashing Passwords with Fast Algorithms
SHA-256 can compute billions of hashes per second on a GPU. If your password database leaks, attackers will brute-force every password in hours.
Benchmark (NVIDIA RTX 4090):
- SHA-256: 15 billion hashes/second
- bcrypt (cost 10): ~60,000 hashes/second
That’s a 250,000x difference. Use bcrypt/Argon2 for passwords.
Forgetting to Encode Input Properly
Text encoding matters. “hello” encoded as UTF-8 vs. UTF-16 produces different hashes.
// Consistent encoding
const hash = crypto.createHash('sha256')
.update(text, 'utf8') // Explicit encoding
.digest('hex');
Pro Tips
Hashing Large Files Efficiently
Don’t load entire files into memory. Stream them instead.
const crypto = require('crypto');
const fs = require('fs');
function hashFile(filename) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha256');
const stream = fs.createReadStream(filename);
stream.on('data', chunk => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
stream.on('error', reject);
});
}
const fileHash = await hashFile('large-file.zip');
This works for gigabyte-sized files without memory issues.
Verifying Downloaded Files
Always verify checksums for downloaded software.
# macOS
curl -O https://example.com/software.dmg
shasum -a 256 software.dmg
# Linux
wget https://example.com/software.tar.gz
sha256sum software.tar.gz
# Compare output to published checksum
If they don’t match, delete the file. It’s either corrupted or compromised.
Content-Addressable Storage
Use hashes as filenames to ensure uniqueness and easy deduplication.
async function saveFile(content) {
const hash = crypto.createHash('sha256')
.update(content)
.digest('hex');
const filename = `uploads/${hash.slice(0, 2)}/${hash.slice(2, 4)}/${hash}.jpg`;
// Path: uploads/2c/f2/2cf24dba5fb0a30e...jpg
await fs.promises.writeFile(filename, content);
return hash;
}
This pattern prevents filename collisions and makes duplicate detection trivial.
HMAC for Message Authentication
When you need to verify both integrity and authenticity, use HMAC (Hash-based Message Authentication Code).
const hmac = crypto.createHmac('sha256', secretKey)
.update(message)
.digest('hex');
HMAC uses a secret key, so only parties with the key can generate valid hashes. This prevents tampering.
Use case: API request signatures. The client signs the request with a shared secret, and the server verifies the signature.
Real-World Examples
Blockchain Proof-of-Work
Bitcoin mining is essentially finding a hash that starts with a certain number of zeros.
function mine(data, difficulty) {
let nonce = 0;
const target = '0'.repeat(difficulty);
while (true) {
const hash = crypto.createHash('sha256')
.update(data + nonce)
.digest('hex');
if (hash.startsWith(target)) {
return { nonce, hash };
}
nonce++;
}
}
// Find hash starting with "000"
const result = mine('Block data', 3);
console.log(result);
// { nonce: 12345, hash: "000a3f7d9c..." }
This is computationally expensive by design - it secures the blockchain.
Git Commit Hashing
Every Git commit has a SHA-1 hash based on its content.
# See commit hash
git log --oneline
# a3f7d9c Add feature
# Show what's hashed
git cat-file commit a3f7d9c
# tree <hash>
# parent <hash>
# author ...
# committer ...
#
# Add feature
The hash is deterministic - if the commit content changes, the hash changes.
CDN Cache Busting
Append content hashes to filenames for cache invalidation.
// Before: style.css (cached forever - bad)
// After: style.2cf24dba.css (new hash = new file)
const hash = crypto.createHash('md5')
.update(fileContent)
.digest('hex')
.slice(0, 8);
const filename = `style.${hash}.css`;
When you update the CSS, the hash changes, browsers fetch the new file. Old versions remain cached.
Frequently Asked Questions
Can two different inputs produce the same hash?
Theoretically yes (infinite inputs, finite outputs), but practically no for modern algorithms. With SHA-256, you’d need to compute 2^128 hashes to have a 50% collision chance, which requires more energy than the sun produces.
Can you reverse a hash to get the original input?
No. Hash functions are one-way by design. You can’t mathematically reverse them. However, attackers can use rainbow tables (precomputed hashes) or brute force to find inputs that match a hash.
Why is MD5 still used if it is broken?
MD5 is broken for security (collision attacks), but it’s still fine for non-security uses like checksums, ETags, and cache keys. It’s faster than SHA-256 and “good enough” when security isn’t a concern.
How do attackers crack password hashes?
Attackers use three primary methods: rainbow tables (precomputed hashes for common passwords), brute force (trying every combination, which is fast with GPUs), and dictionary attacks (trying common passwords first). Slow hashing with bcrypt or Argon2 makes these attacks impractical.
What is the difference between hashing and encryption?
Hashing is one-way (irreversible), encryption is two-way (reversible with a key). Use hashing for integrity verification and password storage. Use encryption for confidentiality.
Should I use SHA-256 or SHA-512?
SHA-256 is standard and plenty secure for most uses. SHA-512 is overkill unless you’re in a high-security environment (government, finance). SHA-512 is faster on 64-bit processors but produces larger outputs.
What is a salt and why do I need it?
A salt is random data added to the input before hashing. It prevents rainbow table attacks by making every hash unique, even for identical passwords. Modern password hashing libraries (bcrypt/Argon2) handle salting automatically.
Can I use hashes as database primary keys?
Yes, but it’s usually a bad idea. Hashes are long (32-64 characters), making indexes larger and slower than integer keys. Use hashes for content addressing, not primary keys.
Use the Online Generator
Need to quickly hash text or verify a file checksum?
Use our Free Hash Generator. It supports MD5, SHA-1, SHA-256, and SHA-512 with instant results. Perfect for verifying downloads, generating checksums, or learning how different algorithms compare. No installation required - runs entirely in your browser.
Related Calculators
Related Articles
- Color Theory Basics for Web Design
Master color theory for web design including the color wheel, complementary palettes, WCAG contrast requirements, and color psychology to create effective interfaces.
- How to Minify CSS for Faster Websites
Learn what CSS minification does, why it speeds up your website, what it removes from your stylesheets, and best practices for minifying CSS in production.
- Cron Job Examples for Common Tasks (Copy-Paste Ready)
Practical cron job examples with clear explanations. Copy-paste ready crontab schedules for backups, reports, cleanup, monitoring, and automation tasks.
- Common JSON Syntax Errors and How to Fix Them
Fix JSON syntax errors fast with this developer guide. Learn the top 5 JSON parsing errors, before/after examples, and debugging techniques to validate JSON instantly.
Share this article
Have suggestions for this article?