DevDash

By the DevDash Team · Last updated July 2026

Hash Functions Explained: MD5, SHA-256, and How Hashing Works

Hash functions are one of the most fundamental building blocks in computer science and cryptography. Every time you download a file and verify its checksum, log into a website, or interact with a blockchain, hash functions are doing critical work behind the scenes. This guide covers what hash functions are, how the major algorithms compare, where they are used in practice, and the mistakes developers make when applying them to security problems like password storage.

What Is a Hash Function?

A hash function is a mathematical algorithm that takes an input (often called a "message") of any size and produces a fixed-length output called a hash, digest, or checksum. You can hash a single character or an entire multi-gigabyte file, and the output will always be the same length for a given algorithm.

Think of a hash function as a fingerprint generator for data. Just as a human fingerprint uniquely identifies a person (in practice), a hash uniquely identifies a piece of data. Change even a single bit of the input, and the output changes completely.

Here is what a SHA-256 hash looks like in practice:

Input:  "hello"
SHA-256: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Input:  "Hello"
SHA-256: 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969

Changing just one character (lowercase "h" to uppercase "H") produces a completely different hash. This is not a small change to the output — it is an entirely unrelated string.

Essential Properties of Hash Functions

Deterministic

The same input always produces the same output. Hash hello with SHA-256 today, tomorrow, or on a different machine — the result will be identical every time. This property is what makes hash functions useful for verification: you can independently compute a hash and compare it to an expected value.

Fixed-length output

Regardless of whether the input is one byte or one terabyte, the hash output is always the same size. MD5 always produces 128 bits. SHA-256 always produces 256 bits. This fixed-length property makes hashes practical for indexing, comparison, and storage — you know exactly how much space a hash will occupy.

One-way (pre-image resistance)

Given a hash output, it should be computationally infeasible to determine the original input. You cannot "reverse" or "decrypt" a hash. This is a fundamental distinction from encryption, and it is why hashing is used for password storage — even if an attacker obtains the stored hashes, they cannot directly recover the passwords.

Avalanche effect

A small change in the input produces a dramatically different output. This is not a minor shift — flipping a single bit should change roughly half the bits in the hash. The avalanche effect makes it impossible to infer relationships between similar inputs by examining their hashes, which is critical for security applications.

Collision resistance

It should be computationally infeasible to find two different inputs that produce the same hash output. Since hash functions map an infinite input space to a finite output space, collisions mathematically must exist (pigeonhole principle) — but a good hash function makes finding them practically impossible.

Hash Algorithm Comparison: MD5 vs SHA-1 vs SHA-256 vs SHA-512

AlgorithmOutput SizeSpeedSecurity StatusCommon Use Cases
MD5128-bit (32 hex chars)Very fastBroken (2004)Checksums, legacy systems
SHA-1160-bit (40 hex chars)FastBroken (2017)Git (legacy), fingerprints
SHA-256256-bit (64 hex chars)ModerateSecureTLS, Bitcoin, digital signatures
SHA-512512-bit (128 hex chars)Moderate*SecureHigh-security, 64-bit systems

*SHA-512 is often faster than SHA-256 on 64-bit processors because it operates on 64-bit words natively.

MD5: Fast but Cryptographically Broken

MD5 (Message Digest Algorithm 5) was designed in 1991 by Ronald Rivest and produces a 128-bit hash. For over a decade it was the default choice for checksums, digital signatures, and password hashing. That changed in 2004 when Chinese researchers demonstrated practical collision attacks, proving they could generate two different inputs with the same MD5 hash.

Since then, the attacks have only gotten worse. In 2012, the Flame malware exploited an MD5 collision to forge a Microsoft code-signing certificate. Today, generating an MD5 collision takes seconds on commodity hardware.

Should you still use MD5? For security purposes, absolutely not. But MD5 remains useful and widely used for non-security applications: verifying file downloads (where the threat model does not include an attacker who can modify both the file and the checksum), generating cache keys, and creating quick content-based identifiers. It is extremely fast and available everywhere. Just do not rely on it to prove that data has not been tampered with by a motivated adversary.

SHA-1: Deprecated but Still Everywhere

SHA-1 (Secure Hash Algorithm 1) produces a 160-bit hash and was the dominant cryptographic hash from the mid-1990s through the 2010s. Theoretical attacks emerged in 2005, and in 2017 Google's SHAttered project demonstrated the first practical SHA-1 collision, creating two different PDF files with the same SHA-1 hash.

All major browser vendors stopped accepting SHA-1 TLS certificates in 2017. NIST formally deprecated SHA-1 for digital signatures in 2011. Yet SHA-1 remains deeply embedded in existing systems. Git, for example, historically used SHA-1 for commit hashes and is only now transitioning to SHA-256. Many package managers and legacy APIs still reference SHA-1 fingerprints.

The practical advice: do not choose SHA-1 for new projects. When you encounter it in existing systems, evaluate whether the security implications matter for your specific use case. Git's use of SHA-1 for content addressing is not a security vulnerability in most threat models, but using SHA-1 for certificate pinning would be.

SHA-256: The Current Standard

SHA-256 is part of the SHA-2 family, designed by the NSA and published by NIST in 2001. It produces a 256-bit (32-byte) hash and has no known practical vulnerabilities. SHA-256 is the workhorse of modern cryptography and the default choice when you need a secure hash function.

Where SHA-256 is used:

  • TLS/SSL certificates — the certificate chain your browser verifies on every HTTPS connection uses SHA-256
  • Bitcoin and blockchain— Bitcoin's proof-of-work mechanism is built on double SHA-256 hashing
  • Digital signatures — signing a SHA-256 hash of a document rather than the entire document
  • Software distribution — package managers like npm, pip, and apt verify downloads using SHA-256 checksums
  • HMAC authentication — API request signing (e.g., AWS Signature Version 4) uses HMAC-SHA256

For most developers, SHA-256 should be the default hash function for any new project that requires cryptographic hashing. You can generate SHA-256 hashes instantly in the browser to verify your implementations or check file integrity.

SHA-512: Extra Security Margin

SHA-512 is SHA-256's larger sibling, producing a 512-bit (64-byte) hash. It uses the same underlying structure but operates on 64-bit words instead of 32-bit words. This makes SHA-512 counterintuitively faster than SHA-256 on 64-bit processors — modern CPUs handle 64-bit operations natively, so SHA-512 processes data in larger chunks per cycle.

Choose SHA-512 when you need an extra security margin against theoretical future attacks, when you are working primarily on 64-bit systems and want the performance benefit, or when compliance requirements specify it. Many Linux distributions use SHA-512 for /etc/shadow password hashes by default.

In practice, SHA-256 and SHA-512 are both considered equally secure against current attack methods. The choice between them is more about performance characteristics and output length requirements than security.

Real-World Use Cases for Hash Functions

File integrity verification

When you download software, the publisher often provides a SHA-256 hash alongside the file. After downloading, you hash the file locally and compare the result. If the hashes match, you know the file was not corrupted during transfer and has not been tampered with (assuming the hash itself was obtained through a trusted channel).

# Linux/macOS
sha256sum downloaded-file.tar.gz
# Compare the output with the published hash

Password storage

Storing plaintext passwords is a critical security failure. Instead, applications store a hash of each password. When a user logs in, the application hashes the submitted password and compares it to the stored hash. If the hashes match, the password is correct — but the actual password is never stored. This means even if the database is breached, attackers do not get raw passwords.

Digital signatures

Signing a large document directly with asymmetric cryptography is slow. Instead, digital signature algorithms hash the document first (producing a small, fixed-size digest) and sign the hash. The recipient hashes the document independently and verifies the signature against their computed hash.

Data deduplication

Cloud storage providers and backup systems use hashes to identify duplicate files. If two files produce the same SHA-256 hash, they are (with astronomically high probability) identical. This allows storing a single copy and referencing it from multiple locations, saving significant storage.

Blockchain

Every block in a blockchain contains the SHA-256 hash of the previous block, creating an immutable chain. Changing any historical block would change its hash, which would invalidate every subsequent block. This hash-based linking is what makes blockchains tamper-evident.

HMAC for API authentication

Hash-based Message Authentication Code (HMAC) combines a hash function with a secret key to verify both the integrity and authenticity of a message. APIs like AWS, Stripe, and GitHub webhooks use HMAC-SHA256 to sign requests — the recipient can verify that the request came from a legitimate source and was not modified in transit.

Hashing vs Encryption: They Are Not the Same Thing

This is one of the most common misconceptions in software development. Hashing and encryption solve fundamentally different problems, and confusing them leads to serious security vulnerabilities.

Hashing is one-way. You put data in, you get a fixed-length digest out, and there is no key or mechanism to reverse the process. The original data is gone — what remains is a fingerprint.

Encryption is two-way. You encrypt data with a key, and anyone with the correct key can decrypt it to recover the original. The original data is preserved in a scrambled form.

When to use hashing: password storage, data integrity verification, digital signatures, content addressing. You need to verify or compare data but do not need to recover the original input.

When to use encryption: storing sensitive data you need to read later (credit card numbers, personal information), secure communication (TLS), protecting files at rest. You need both confidentiality and the ability to retrieve the original data.

A common mistake: "encrypting" passwords with AES or another symmetric cipher and storing the encryption key alongside the database. If the database is breached, the attacker gets the key too, and all passwords are instantly recoverable. Hashing eliminates this class of vulnerability entirely because there is no key to steal.

Password Hashing: Why Raw SHA-256 Is Not Enough

If hash functions are one-way, why can't you just SHA-256 a password and store it? The problem is speed. SHA-256 is designed to be fast — a modern GPU can compute billions of SHA-256 hashes per second. An attacker who obtains your hash database can try every possible password at enormous speed.

The salt problem

Without a salt (a random string appended to the password before hashing), identical passwords produce identical hashes. If two users both choose "password123", their stored hashes are the same — and an attacker can precompute hashes for common passwords in a rainbow table and look them up instantly. Adding a unique, random salt per user means the same password produces a different hash for every user.

Purpose-built password hashing algorithms

The security community has built specialized algorithms for password hashing that solve both the speed and salt problems:

  • bcrypt — based on Blowfish, includes built-in salt, configurable cost factor (work factor). The standard choice for most applications. Used by password generators and authentication systems everywhere.
  • scrypt — adds memory-hardness on top of computational cost. Makes GPU-based attacks more expensive because it requires significant RAM, not just processing power.
  • Argon2 — winner of the 2015 Password Hashing Competition. Configurable for time cost, memory cost, and parallelism. The current best practice for new applications.
# Python example: password hashing with bcrypt
import bcrypt

# Hash a password
password = b"user_password_here"
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)

# Verify a password
if bcrypt.checkpw(password, hashed):
    print("Password matches")

The key takeaway: never implement password storage with raw SHA-256, MD5, or any general-purpose hash function — even with a salt. Use bcrypt, scrypt, or Argon2.

Generating and Verifying Hashes in Practice

Most programming languages and operating systems provide built-in tools for hash generation. Here are common approaches:

Command line

# macOS / Linux
echo -n "hello" | sha256sum
echo -n "hello" | md5sum
shasum -a 512 filename.txt

# Windows PowerShell
Get-FileHash filename.txt -Algorithm SHA256

JavaScript (Web Crypto API)

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("");
}

// Usage
const hash = await sha256("hello");
// "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

Python

import hashlib

# String hashing
result = hashlib.sha256(b"hello").hexdigest()

# File hashing
with open("file.txt", "rb") as f:
    file_hash = hashlib.sha256(f.read()).hexdigest()

For quick hash generation and verification without writing code, you can use DevDash's hash generator — paste any text, select your algorithm (MD5, SHA-1, SHA-256, SHA-512), and get the result instantly. All computation happens client-side in your browser, so your data never leaves your machine.

Hash Collisions: What They Are and Why They Matter

A hash collision occurs when two different inputs produce the same hash output. This might sound unlikely, but it is mathematically guaranteed to happen eventually. A 256-bit hash function has 2256 possible outputs — an astronomically large number, but still finite. Since the number of possible inputs is infinite, multiple inputs must map to the same output (the pigeonhole principle).

The question is not whether collisions exist — it is whether an attacker can find them. There are two levels of collision attacks:

  • Collision attack — the attacker finds any two inputs that hash to the same output. This is what was demonstrated against MD5 (2004) and SHA-1 (2017).
  • Pre-image attack — the attacker finds an input that hashes to a specific target output. This is much harder and has not been achieved against any standard hash function, even MD5.

The birthday paradox explains why collision attacks are easier than you might expect. Due to probability mathematics, finding a collision in an n-bit hash function requires approximately 2n/2 attempts, not 2n. For MD5 (128-bit), that means roughly 264 attempts — well within reach of modern hardware.

For SHA-256, a collision attack would require approximately 2128 operations. To put that in perspective, if every computer on Earth ran collision searches continuously, it would take longer than the age of the universe to find one. This is why SHA-256 remains the trusted standard for security-critical applications.

How to Choose the Right Hash Function

The right hash function depends entirely on your use case:

  • File checksums (non-adversarial) — MD5 or SHA-256. If no one is trying to trick you, MD5 is fine. If you need tamper-detection, use SHA-256.
  • Digital signatures and certificates — SHA-256. This is the minimum standard required by browsers and certificate authorities.
  • Password storage — bcrypt, scrypt, or Argon2. Not SHA-256, not MD5. Never a general-purpose hash function.
  • HMAC/API authentication — HMAC-SHA256. Widely supported and the standard for webhook signatures and request authentication.
  • Content addressing and deduplication — SHA-256 for security-sensitive contexts, or even MD5/xxHash for internal performance-critical systems where adversarial collisions are not a concern.
  • JWT signing — HS256 (HMAC-SHA256) for symmetric signing, RS256 (RSA with SHA-256) for asymmetric.

When in doubt, use SHA-256. It is fast enough for virtually all applications, secure against all known attacks, and universally supported across every programming language and platform.

Generate Hashes Instantly in Your Browser

MD5, SHA-1, SHA-256, and SHA-512 — paste any text, get the hash. No sign-up required. All processing happens client-side.

Open Hash Generator