• Enterprise
  • Prețuri
AutentificareCreează cont
⌘K
Introducere
Primii pași
  • Instalare
  • Structura proiectului
Ghiduri
  • Elemente MDX
Integrări
  • AI Gateway
  • Hazeloft CDN
  • Security
    • Capabilități
    • Înregistrare
    • Dashboard
    • EmailEncrypt
    • Bune practici
  • Translator

Se încarcă documentația

Aplicații

  • DesktopÎn curând
  • Mail
  • Toate produsele

Platformă

  • AI
  • Agents
  • CDN
  • Host
  • Observability
  • Connect
  • Workflows
  • Plugin

Companie

  • Enterprise
  • Prețuri
  • Clienți
  • Despre noi

Resurse

  • Documentație
  • Contact

Legal & Trust

  • Documente legale
Alege tema afișată
IntegrăriSecurity

Protecția înregistrării

Apărarea completă a paginii de sign-up într-o singură funcție: honeypot, detectare boți, validare email și nume, limită per adresă și verificare de domeniu — plus challenge-ul din browser.

Ultima actualizare: 2 august 2026

Înregistrarea e ruta pe care o atacă toată lumea, fiindcă e singura care creează conturi gratuit. protectSignup() îți dă toată apărarea ei într-o singură funcție, iar challenge() adaugă un strat în plus care cere clientului să demonstreze că nu e un simplu script.

#protectSignup()

O singură funcție care rulează întreaga stivă anti-bot de sign-up și îți întoarce un verdict.

app/api/sign-up/route.ts
import { protectSignup, createConfigClient } from "@hazeloft/security";
 
const configClient = createConfigClient(); // ia setările din dashboard
 
export async function POST(request: Request) {
  const body = await request.json();
  const verdict = await protectSignup({
    headers: request.headers,
    body,                              // { email, name }
    config: await configClient.get(),
  });
 
  if (verdict.conclusion === "deny") {
    return Response.json({ error: "Înregistrare respinsă" }, { status: 403 });
  }
 
  // creează contul
}

#Ce verifică

VerificareCe prinde
honeypotboți care completează un câmp-capcană invizibil pentru om
detectare boțiclienți automatizați după cum se prezintă
validare emailadrese temporare, invalide sau prea lungi
validare numenume generate aleator
limită per adresăprea multe conturi pe aceeași adresă de email
verificare domeniudomenii de email inexistente

Fiecare se pornește, se oprește sau se pune pe „doar observ" din dashboard, separat.

#Verdictul

verdict.conclusion      // "allow" sau "deny"
verdict.canonicalEmail  // adresa normalizată (folosește-o la verificarea de cont unic)
verdict.skipped         // verificări care n-au putut rula acum — vezi mai jos

Urmărește `skipped`

Câteva verificări (limita per adresă, verificarea de domeniu) au nevoie de o rundă către serviciul Hazeloft. Dacă serviciul e momentan indisponibil, cererea trece (nu vrem ca o problemă de rețea să blocheze înregistrările), iar verificarea respectivă apare în verdict.skipped. Loghează-l, ca să știi când o protecție n-a rulat — nu presupune tăcut că totul a mers.

#Honeypot

Pui în formular un câmp invizibil pentru om. Un om nu-l vede, deci nu-l completează; un bot care completează automat tot formularul îl completează și pe el. Formularul trimite un antet special doar dacă acel câmp a fost completat:

signup-form.tsx
import { SIGNUP_HONEYPOT_HEADER } from "@hazeloft/security";
 
// un input ascuns, cu tabIndex={-1} și autoComplete="off"
const headers: Record<string, string> = { "content-type": "application/json" };
if (honeypotValue) headers[SIGNUP_HONEYPOT_HEADER] = honeypotValue;

#Limită per adresă de email

Numără încercările pe aceeași adresă, tratând variațiile ca fiind aceeași: la gmail, j.doe+1@gmail.com, jd.oe@gmail.com și jdoe@gmail.com lovesc un singur contor.

Ce prinde

Prinde fermele de conturi făcute pe o singură căsuță de email cu variații de puncte și +alias — vectorul care generează cele mai multe conturi false. Un atacator cu adrese reale distincte trece pe lângă ea, dar pe acela îl prind challenge-ul și verificarea de domeniu.

#Verificare de domeniu

Un domeniu de email inexistent nu poate primi mail — deci nu are cum să fie real. Verificarea îl prinde chiar dacă nu e pe nicio listă de domenii temporare, fiindcă întreabă direct DNS-ul. Poți alege din dashboard cât de strict e (doar domenii inexistente, sau și domenii fără server de mail configurat).

#Cont unic pe forma canonică

Ca să nu lași aceeași persoană să-și facă mai multe conturi cu variații de email, folosește forma canonică peste tot unde cauți un user după email — nu doar la sign-up:

import { canonicalizeAuthEmail } from "@hazeloft/security";
 
// la sign-up, login, resetare de parolă și retrimiterea verificării
record.email = canonicalizeAuthEmail(record.email);

Pe toate căile, nu doar la înregistrare

Dacă sign-up-ul normalizează adresa dar login-ul nu, un utilizator real care își scrie gmail-ul cu puncte nu se mai poate loga la contul creat fără ele. Aplic-o consecvent.

#Challenge

Toate verificările de mai sus se uită la ce trimite clientul. Dar un script poate trimite exact aceiași octeți ca un browser real. Challenge-ul e singura piesă în care clientul trebuie să facă ceva — un mic calcul în browser — ca să dovedească faptul că nu e doar un POST aruncat direct pe rută.

#Montare: trei piese

Ai nevoie și de partea de browser:

Terminal
npm i @hazeloft/security-client

1. Un endpoint care emite challenge-ul (pe serverul tău):

app/api/challenge/route.ts
import { issueChallenge } from "@hazeloft/security";
 
export async function GET() {
  const { token, nonce, exp } = await issueChallenge({
    secret: process.env.HAZELOFT_CHALLENGE_SECRET,
    action: "sign-up",
  });
  await recordNonce(nonce, exp); // salvează-l: un tabel, Redis, orice depozit al tău
  return Response.json({ token });
}

2. În browser, formularul cere challenge-ul și îl rezolvă înainte de submit:

signup-form.tsx
import { CHALLENGE_HEADER, fetchAndSolveChallenge } from "@hazeloft/security-client";
 
const header = await fetchAndSolveChallenge({ endpoint: "/api/challenge" });
await fetch("/api/sign-up", {
  method: "POST",
  headers: { "content-type": "application/json", [CHALLENGE_HEADER]: header },
  body: JSON.stringify(form),
});

3. Ruta protejată verifică challenge-ul (aceeași action și secret ca la emitere):

security.ts
import { HazeloftSecurity, challenge } from "@hazeloft/security";
 
export const security = new HazeloftSecurity({
  apiKey: process.env.HAZELOFT_SECURITY_API_KEY,
  rules: [
    challenge({
      secret: process.env.HAZELOFT_CHALLENGE_SECRET,
      action: "sign-up",
      isFresh: consumeNonce, // marchează nonce-ul ca folosit, o singură dată
    }),
  ],
});

Ce garantează, și ce nu

Challenge-ul ridică costul automatizării, nu dovedește că e un om. Un browser automatizat (Playwright, Puppeteer) rezolvă calculul și trece — la fel orice script care își dă osteneala. Valoarea reală e că fiecare încercare costă efort și trebuie cerută de la tine, deci o poți număra și limita. Pune o limită (rateLimit) pe endpoint-ul care emite challenge-uri și combină-l cu restul apărării — nicio piesă nu ține singură.

#Lansează în modul „doar observ" întâi

Dacă pornești challenge-ul direct pe „blochează", vei bloca și clienții care încă nu trimit antetul — pagini rămase în cache, aplicații mobile neactualizate. Pornește-l din dashboard pe „doar observ", urmărește câteva zile ce ar fi blocat, apoi treci pe „blochează". Vezi Best practices.

AnteriorCapabilitățiUrmătorDashboard

Pe această pagină

  • `protectSignup()`
  • Ce verifică
  • Verdictul
  • Honeypot
  • Limită per adresă de email
  • Verificare de domeniu
  • Cont unic pe forma canonică
  • Challenge
  • Montare: trei piese
  • Lansează în modul „doar observ" întâi