If you're checking user passwords against HaveIBeenPwned's breach database, please don't send the full password (or even the full hash) over the wire. Here's why, and the actual protocol that fixes it.
HIBP's Pwned Passwords API uses k-anonymity: you SHA-1 hash the password locally, send only the first 5 characters of the hash, and the API returns every suffix that starts with that prefix (usually 300-900 of them). You compare locally. The full password never leaves your server, and HIBP never sees enough to reconstruct it.
import { createHash } from "node:crypto";
async function isBreached(password) {
const hash = createHash("sha1").update(password).digest("hex").toUpperCase();
const prefix = hash.slice(0, 5);
const suffix = hash.slice(5);
const res = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
const text = await res.text();
return text.split("\n").some(line => line.startsWith(suffix));
}
That's genuinely all it takes — no library needed. I wrapped this (plus password strength scoring) into an endpoint on Validate if you'd rather not maintain the hashing/comparison logic yourself, but honestly, the snippet above will get most people there. Sibling APIs on the same account: QR API and Currency API.
Top comments (0)