Mesrai
Back to blog
// essaySecurity

Weak Password Hashing (MD5/SHA1): Mesrai's Argon2 Suggestion

Real PR catch: password hash with SHA-1 — crackable at 25 billion guesses per second. The Argon2id migration Mesrai recommends. CWE-916.

Mesrai TeamJuly 23, 20268 min read

A password hashing helper. Uses SHA-1 + a salt. The author thought the salt made it safe. Mesrai flagged the algorithm choice — the salt prevents rainbow-table attacks, but does not slow down per-guess hashing.

The vulnerable diff

tsauth/password.ts
// auth/password.ts
import crypto from "crypto";

export function hashPassword(password: string, salt: string): string {
  // BUG: SHA-1 is a fast hash. Optimized for speed, not password storage.
  return crypto.createHash("sha1").update(password + salt).digest("hex");
}

What is wrong

Cryptographic hashes split into two categories. Fast hashes (MD5, SHA-1, SHA-256, SHA-512) are designed to be quick — useful for content integrity, terrible for password storage because they can be guessed billions of times per second. Password hashes (Argon2, scrypt, bcrypt, PBKDF2 with high iterations) are deliberately slow and memory-hard — they cap the attacker at a few thousand guesses per second per GPU. CWE-916 covers the use of the wrong category, CWE-327 the use of broken algorithms in general.

The attack

If an attacker steals the password hash file (SQL injection on the users table, leaked backup, insider threat), they crack offline:

bashexploit
# Hashcat on a single RTX 4090
hashcat -a 0 -m 100 sha1_hashes.txt rockyou.txt
# Speed: ~25,000,000,000 H/s
# Cracks "Password1!" in under a second.
# Cracks the entire rockyou.txt list against 10,000 hashes in 5 minutes.

# Same hash file with Argon2id at memoryCost 65536:
# Speed: ~50 H/s on the same GPU
# Cracking 10,000 hashes against rockyou would take centuries.

The same hash file becomes essentially uncrackable if the cost parameters are tuned to take ~250ms per single guess.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 30 sec ago

[mesrai] [code-review] [Security] [CWE-916, CWE-327] [high]

SHA-1 with a salt is a fast hash, not a password hash. GPU rigs crack
salted SHA-1 at ~25 billion guesses per second. The salt only prevents
rainbow-table attacks; it does not slow per-guess hashing.

Use a memory-hard password KDF. In order of preference for 2026:

  1. Argon2id (winner of the Password Hashing Competition)
  2. scrypt
  3. bcrypt (still acceptable, simpler tuning)

  import { hash, verify, Algorithm } from "argon2";
  export const hashPassword = (pw: string) =>
    hash(pw, { type: Algorithm.Argon2id, memoryCost: 65536,
               timeCost: 3, parallelism: 1 });
  export const verifyPassword = (h: string, pw: string) => verify(h, pw);

These params take ~250ms on a modern CPU — slow enough to make
offline cracking impractical, fast enough that login is imperceptible.

Reference: OWASP Password Storage Cheat Sheet, CWE-916, CWE-327

The fix

tsauth/password.ts (fixed)
// auth/password.ts — fixed
import { hash, verify, Algorithm } from "argon2";

export const hashPassword = (password: string) =>
  hash(password, {
    type: Algorithm.Argon2id,
    memoryCost: 65536, // 64 MiB per guess
    timeCost: 3,
    parallelism: 1,
  });

export const verifyPassword = (hashed: string, password: string) =>
  verify(hashed, password);

Argon2id is the 2026 recommendation from OWASP. The library bundles salt generation and parameter encoding into the output string so verification round-trips cleanly. The parameters above are tuned for ~250ms per guess on a modern CPU — slow enough to defeat offline cracking, fast enough that interactive login feels instant.

Why human review missed it

The bug looks like the author thought about security — there is a salt, the hash function is named, the function signature is clean. The category mistake (fast hash vs memory-hard KDF) is invisible without security training. Mesrai's rule pack catches every call to `createHash` on a password variable, and every use of MD5/SHA-1/SHA-2 family for password storage.

Related rules + further reading

Mesrai rule pack: security/password-hash-algorithm — flags MD5/SHA-1/SHA-2 family used for password storage.

OWASP: Password Storage Cheat Sheet.

CWE-916 (effort) / CWE-327 (algorithm).

Takeaway

Argon2id. Memory-hard, salt-included, ~250ms per guess. Migrate any SHA/MD5 password hashes today — they can already be cracked.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.