Blog

Hashing vs HMAC for Engineers: Choosing Algorithms and Avoiding Common Mistakes


Every engineer encounters hashing early in their career — verifying file integrity, storing passwords, authenticating API requests. But the distinction between a plain cryptographic hash and an HMAC (Hash-based Message Authentication Code) trips up even experienced developers. Choosing the wrong one, or misusing either, can introduce vulnerabilities that are invisible until they're exploited.

This guide breaks down how hashing and HMAC work, when to reach for each, which algorithms to prefer in 2026, and the mistakes that keep showing up in production codebases.

What Is a Cryptographic Hash?

A cryptographic hash function takes arbitrary input and produces a fixed-length digest. The core properties are:

  • Deterministic: The same input always yields the same output.
  • Pre-image resistant: Given a hash, it's computationally infeasible to recover the original input.
  • Collision resistant: It's infeasible to find two different inputs that produce the same hash.

RFC 6234 defines the SHA family of hash algorithms — including SHA-224, SHA-256, SHA-384, and SHA-512 — as well as SHA-based HMAC and HKDF constructions [1]. These algorithms are the workhorses of modern integrity verification.

Hashes are also used as identifiers. RFC 6920 defines a URI scheme for naming digital objects using hash outputs, enabling objects to be authenticated by comparing a fetched resource against its hash-based name [2][3]. This illustrates a purely integrity-focused use case — there's no secret key involved.

What Is HMAC?

HMAC adds a secret key to the hashing process. Defined in RFC 2104 by Krawczyk, Bellare, and Canetti, HMAC provides message authentication — it proves both that a message hasn't been tampered with and that it was produced by someone who holds the secret key [4].

The construction works by hashing the message together with the key in two passes:

HMAC(K, m) = H((K' ⊕ opad) || H((K' ⊕ ipad) || m))

Where K' is the key (padded or hashed to the block size), opad and ipad are fixed padding constants, and H is the underlying hash function. This nested structure is what makes HMAC resistant to length-extension attacks — a vulnerability that affects naive constructions like H(key || message) [4].

Hashing vs HMAC: When to Use Each

The decision comes down to one question: do you need to prove who created the digest?

Use CaseMechanismWhy
File integrity / checksumPlain hash (SHA-256)No secret needed; anyone should be able to verify
Content-addressable storagePlain hashObjects are named by their hash [2]
API request signingHMACProves the request came from an authorized party
Webhook verificationHMACReceiver verifies the sender knows the shared secret
Session token generationHMAC or dedicated KDFPrevents token forgery
Password storageNeither — use bcrypt/scrypt/Argon2Requires deliberate slowness to resist brute force [5]

Choosing the Right Algorithm in 2026

Not all hash functions are created equal. Here's a practical rundown:

SHA-256 / SHA-512

SHA-256 is the default recommendation for most use cases. It's specified in RFC 6234 alongside the broader SHA-2 family [1]. SHA-512 can be faster on 64-bit platforms and provides a larger digest, useful for protocols requiring wider security margins.

SHA-1 and MD5: Retired

MD5 and SHA-1 have known collision vulnerabilities. While you'll still encounter them in legacy systems (e.g., Git historically used SHA-1), neither should be used in new designs for any security purpose. RFC 6151, which updates RFC 2104, documents the weaknesses in MD5-based HMAC constructions [4].

SHA-3 (Keccak)

SHA-3 uses a fundamentally different construction (sponge function) than SHA-2. It's a solid choice when you want algorithm diversity — for instance, if a future break in SHA-2 were discovered, SHA-3 would remain unaffected. However, SHA-256 remains more widely supported in libraries and hardware acceleration.

BLAKE2 / BLAKE3

BLAKE2 and BLAKE3 are high-performance hash functions increasingly popular in applications like file deduplication and build systems. BLAKE3 supports keyed hashing natively, which can serve as an HMAC alternative. However, for interoperability with standards-based protocols, SHA-2 plus HMAC per RFC 2104 remains the safer bet [4].

Common Mistakes Engineers Make

1. Using H(key || message) Instead of HMAC

This is the most dangerous mistake. SHA-256 and other Merkle–Damgård hash functions are vulnerable to length-extension attacks. An attacker who knows H(key || message) can compute H(key || message || padding || attacker_data) without knowing the key. HMAC's double-pass construction explicitly prevents this [4][6].

2. Using Plain Hashes for Authentication

A bare SHA-256(payload) proves nothing about who produced the payload. Any party can compute the same hash. If you're verifying that a webhook actually came from a trusted service, you need HMAC with a shared secret — not a plain hash [6].

3. Using SHA-256 for Password Storage

Fast hashes like SHA-256 are the wrong tool for passwords. An attacker with a GPU can compute billions of SHA-256 hashes per second. Password hashing algorithms like Argon2, bcrypt, and scrypt are intentionally slow and memory-hard, making brute-force attacks impractical [5].

4. Hardcoding or Leaking HMAC Keys

HMAC is only as secure as its key. Keys embedded in client-side code, committed to version control, or transmitted in the clear negate all security benefits. Use environment variables, secret managers (e.g., HashiCorp Vault, AWS Secrets Manager), and rotate keys on a schedule.

5. Comparing Hashes with ==

String comparison operators in most languages short-circuit on the first mismatched character, leaking timing information. Always use a constant-time comparison function (e.g., hmac.compare_digest() in Python, crypto.timingSafeEqual() in Node.js) when verifying hash or HMAC values.

6. Ignoring Algorithm Agility

Hardcoding a single algorithm without a versioning or migration path means a painful transition when that algorithm is eventually deprecated. Tag your hashes or HMAC outputs with an algorithm identifier so you can upgrade incrementally.

Practical Example: Verifying a Webhook with HMAC-SHA256

Most webhook providers (Stripe, GitHub, Slack) use HMAC-SHA256 for payload verification. Here's the pattern in Node.js:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  );
}

Note the use of timingSafeEqual — this prevents the timing side-channel attack described in mistake #5 above.

Quick-Reference Decision Flowchart

  1. Are you storing passwords? → Use Argon2id, bcrypt, or scrypt. Stop here.
  2. Do you need to prove who created the digest? → Use HMAC (with SHA-256 or SHA-512) [4].
  3. Do you only need to verify data integrity? → Use a plain SHA-256 hash [1].
  4. Do you need to name or address content by its hash? → Use SHA-256 with a ni: URI scheme [2][3].
  5. Do you need extreme performance for non-cryptographic integrity? → Consider BLAKE3 with caution regarding interoperability.

Try It Yourself

Understanding these concepts is easier when you can experiment. The Hash Generator on Online Dev Tools lets you compute SHA-256, SHA-512, HMAC, and other digests directly in your browser — useful for verifying test vectors, debugging webhook signatures, or comparing algorithm outputs side by side.

Building a system that shares secrets or sensitive data? The Secure Paste tool provides encrypted, expiring snippets — ideal for sharing HMAC keys with teammates without resorting to plaintext chat messages. And if you need unique identifiers alongside your hashes, the UUID Generator produces RFC 4122-compliant UUIDs on demand.

Key Takeaways

  • Plain hashes verify integrity. HMAC verifies integrity + authenticity.
  • Never construct H(key || message) yourself — use HMAC as defined in RFC 2104 [4].
  • SHA-256 is the default choice for general-purpose hashing in 2026. SHA-1 and MD5 are retired.
  • Passwords require purpose-built algorithms (Argon2, bcrypt, scrypt), not fast hashes [5].
  • Always use constant-time comparison when verifying digests.
  • Design for algorithm agility from day one.

Getting hashing right isn't glamorous, but it's foundational. The difference between a secure system and a vulnerable one often comes down to choosing the right primitive — and using it correctly.

Sources

  1. [1] RFC 6234 - US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)
  2. [2] RFC 6920: Naming Things with Hashes
  3. [3] RFC 6920 - Naming Things with Hashes
  4. [4] RFC 2104 - HMAC: Keyed-Hashing for Message Authentication
  5. [5] Choosing the Right Hashing Algorithm (SHA-256 vs HMAC vs Password Hashing) – ToolsAreUs
  6. [6] Understanding Hash, MAC, and HMAC in Security - NashTech Blog