ViraStack
  • Akademi
  • Hakkında
  • Destek
Genel BakışBaşlangıçÖrnekler

Başlangıç

  • Giriş
  • 1.0'dan geçiş
  • llms.txt
  • llms-full.txt

Kullanım

  • useViraPassword()
  • Güç
  • Parola tekrarı
  • React Hook Form
  • Erişilebilirlik
  • Özelleştirme

Sunucu

  • Sunucu doğrulaması
  • Sızmış parolalar

Başvuru

  • Yardımcılar
  • Tipler
  • UI kütüphaneleri
  • Karşılaştırma

Sızmış parolalar

İsteğe bağlı Have I Been Pwned kontrolü, k-anonymity ile: @virastack/password/pwned.

Kullanım

Siz çağırmadıkça kapalıdır. Ücretsiz Pwned Passwords range API'sini kullanır; anahtar ve bağımlılık gerekmez.

// components/leak-check.tsx
"use client";
 
import { useState, useTransition } from "react";
 
import { useViraPassword } from "@virastack/password";
import type { PwnedResult } from "@virastack/password/pwned";
 
import { checkPassword } from "@/app/signup/pwned-action";
 
export function LeakCheck() {
  const [result, setResult] = useState<PwnedResult | null>(null);
  const [pending, startTransition] = useTransition();
  const { inputProps, btnProps } = useViraPassword({
    inputProps: { name: "password", defaultValue: "password123" },
  });
 
  function onSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const password = String(new FormData(event.currentTarget).get("password"));
    startTransition(async () => setResult(await checkPassword(password)));
  }
 
  return (
    // POST: a submit before hydration never puts the password in the URL.
    <form method="post" onSubmit={onSubmit}>
      <label htmlFor={inputProps.id}>Password</label>
      <div>
        <input {...inputProps} />
        <button {...btnProps} />
      </div>
      <button type="submit" disabled={pending}>
        Check leaks
      </button>
 
      <p aria-live="polite">
        {result?.status === "pwned" &&
          `Seen ${result.count.toLocaleString("en")} times in data breaches.`}
        {result?.status === "safe" && "Not found in known breaches."}
        {/* Fail open: never block the user because the check failed. */}
        {result?.status === "unknown" &&
          `Could not check (${result.reason}). The form still works.`}
      </p>
    </form>
  );
}
// app/signup/pwned-action.ts
"use server";
 
import { checkPwned, type PwnedResult } from "@virastack/password/pwned";
 
// From the server, Have I Been Pwned never sees the user's IP.
// Only the first 5 characters of the SHA-1 hash are sent.
export async function checkPassword(password: string): Promise<PwnedResult> {
  return checkPwned(password, { timeout: 3000 });
}
checkPwned(password, {
  signal,         // AbortSignal
  timeout: 3000,  // ms, varsayılan 5000
  fetch,          // özel fetch (test, proxy)
})

Sonuç

SonuçAnlamı
{ status: "pwned", count }Sızıntılarda count kez görülmüş
{ status: "safe" }Listede yok
{ status: "unknown", reason }empty, unsupported (crypto.subtle veya fetch yok), network, timeout, aborted, rate-limited, http

Hiç hata fırlatmaz. unknown durumunda kullanıcının devam etmesine izin verin (fail open).

Gizlilik ve sınırlar

  • Parolanın SHA-1 özetinin yalnızca ilk 5 karakteri gönderilir; parola ve tam özet asla çıkmaz. Yanıtlar dolgu (padding) içerir.
  • Sunucudan çağırın. Tarayıcıdan çağrılırsa Have I Been Pwned kullanıcının IP'sini görür.
  • crypto.subtle yalnızca HTTPS ve localhost'ta vardır; aksi halde sonuç unknown olur.
  • Her tuş vuruşunda çağırmayın; gönderimde kontrol edin.
ÖncekiSunucu doğrulamasıSonrakiYardımcılar

Bu sayfada

  • Kullanım
  • Sonuç
  • Gizlilik ve sınırlar
Hata mı var? Düzeltin