Files
Wabenhain/src/lib/password-breach-check.ts
T
Nick Szaunig c7927f2883 feat: Wabenhain Honig-Shop Anwendung
Vollstaendige Next.js/Prisma-E-Commerce-Anwendung fuer eine Imkerei:
Shop, Warenkorb, Stripe-Checkout, Kundenkonto mit 2FA, Admin-Bereich
(Produkte, Ernten, Chargen, Bestellungen, Gutscheine, admin-editierbare
Website-Inhalte), i18n (DE/EN), Rate-Limiting/CSP/Audit-Log-Haertung,
PostgreSQL- und MySQL-Unterstuetzung ueber austauschbare Prisma-
Treiber-Adapter, Unit-/Komponenten-/E2E-Tests.
2026-08-17 14:30:28 +02:00

40 lines
1.4 KiB
TypeScript

import { createHash } from "node:crypto";
const HIBP_TIMEOUT_MS = 3000;
/**
* Prueft ueber die Have-I-Been-Pwned-API (k-Anonymitaet: nur die ersten 5
* Hex-Zeichen des SHA-1-Hashes verlassen den Server, niemals das Passwort
* selbst oder der volle Hash) ob ein Passwort in bekannten Datenlecks
* vorkommt. Bei Netzwerk- oder Zeitueberschreitungsfehlern wird bewusst
* "nicht kompromittiert" zurueckgegeben (fail-open) — ein Ausfall des
* externen Diensts darf Registrierung oder Passwort-Reset nicht blockieren.
*/
export async function isPasswordBreached(password: string): Promise<boolean> {
try {
const sha1 = createHash("sha1").update(password, "utf8").digest("hex").toUpperCase();
const prefix = sha1.slice(0, 5);
const suffix = sha1.slice(5);
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), HIBP_TIMEOUT_MS);
try {
const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`, {
signal: controller.signal,
headers: { "Add-Padding": "true" },
});
if (!response.ok) return false;
const body = await response.text();
return body.split("\n").some((line) => {
const [lineSuffix, count] = line.trim().split(":");
return lineSuffix === suffix && Number(count) > 0;
});
} finally {
clearTimeout(timeout);
}
} catch {
return false;
}
}