Torna al blog
Sicurezza

Guida pratica per proteggere le registrazioni SaaS B2B

Josselin Liebe
Josselin Liebe

Il mese scorso, un cliente SaaS B2B si e rivolto a noi con un problema familiare: le registrazioni sembravano normali in superficie, ma i crediti gratuiti sparivano rapidamente. Analizzando i log abbiamo scoperto un attacco coordinato — decine di registrazioni dagli stessi subnet /24, email usa e getta in rotazione e un attaccante che riutilizzava infrastrutture su piu account falsi.

Questo articolo illustra la stack di difesa che abbiamo raccomandato. Nomi e identificatori sono anonimizzati, ma il flusso e pronto per la produzione.

Cosa ha facilitato l'attacco

Tre fattori hanno giocato a favore dell'attaccante:

  1. Tutti i crediti gratuiti concessi alla registrazione — nessuna frizione, nessuna verifica, valore immediato.
  2. Nessun legame al dispositivo — la stessa impronta del browser poteva registrarsi ripetutamente.
  3. Controlli IP assenti o mal configurati — dietro il proxy Cloudflare (nuvola arancione), l'app leggeva IP proxy invece dell'indirizzo reale del visitatore.

Proteggere le registrazioni non e una soluzione unica, ma stratificare segnali che si rafforzano a vicenda.

Priorita 1: leggere l'IP reale

Se la tua app e dietro Cloudflare con proxy attivo (nuvola arancione), req.ip o X-Forwarded-For da soli non bastano. Cloudflare invia l'IP reale del visitatore nell'header CF-Connecting-IP.

function getClientIp(request: Request): string {
  const cfIp = request.headers.get("cf-connecting-ip");
  if (cfIp) return cfIp;

  const forwarded = request.headers.get("x-forwarded-for");
  if (forwarded) return forwarded.split(",")[0].trim();

  return "0.0.0.0";
}

Ogni controllo successivo — rate limit, reputazione IP, blocco — dipende dal fare correttamente questo primo passo.

Priorita 2: valutare l'IP alla registrazione

Esegui la API Veille IP Reputation in modo asincrono durante la registrazione. Se possibile, non bloccare la risposta HTTP su di essa; accoda il controllo e agisci entro secondi.

const API_KEY = process.env.VEILLE_API_KEY!;
const BASE_URL = "https://api.veille.io/v1";

async function scoreIp(ip: string) {
  const response = await fetch(
    `${BASE_URL}/intelligence/ip?query=${encodeURIComponent(ip)}`,
    { headers: { "x-api-key": API_KEY } }
  );
  return response.json();
}

Il threat_score aggrega i segnali rilevati:

Score Significato
0 IP residenziale o aziendale pulita
1–25 Segnali minori (datacenter, VPN)
25–50 Rischio moderato (segnali multipli)
50+ Rischio alto (Tor, abusatore noto, proxy) — bloccare o richiedere verifica extra

Nei log del nostro cliente, le IP attaccanti si raggruppavano in range /24 con score superiori a 50. Bloccare la subnet dopo abusi ripetuti, non solo la singola IP, ha ridotto rapidamente il volume.

Priorita 3: integrare FingerprintJS

Installa FingerprintJS sulle pagine di registrazione e login. Memorizza il visitorId ad ogni tentativo.

import FingerprintJS from "@fingerprintjs/fingerprintjs";

async function getFingerprint(): Promise<string> {
  const fp = await FingerprintJS.load();
  const result = await fp.get();
  return result.visitorId;
}

Poi applica rate limit su IP e impronta digitale:

  • Dopo 5 tentativi falliti di registrazione o login dallo stesso fingerprint o IP → blocco per 24 ore.
  • Registra entrambi i valori ad ogni tentativo per pivotare facilmente nel back office.

Priorita 4: validare l'email in modo asincrono

Le email usa e getta non fermeranno un attaccante determinato, ma filtrano gran parte dell'abuso a basso sforzo. Chiama la API Veille Email Validation in parallelo al controllo IP:

async function validateEmail(email: string) {
  const response = await fetch(
    `${BASE_URL}/intelligence/email?query=${encodeURIComponent(email)}`,
    { headers: { "x-api-key": API_KEY } }
  );
  return response.json();
}

// Nel handler di registrazione:
const [ipResult, emailResult] = await Promise.all([
  scoreIp(clientIp),
  validateEmail(email),
]);

if (emailResult.disposable || emailResult.risk_score > 70) {
  await blockIpTemporarily(clientIp, "disposable_email");
  return { error: "Registrazione bloccata" };
}

if (ipResult.threat_score >= 50) {
  await blockIpTemporarily(clientIp, "high_threat_ip");
  return { error: "Registrazione bloccata" };
}

Esegui questi controlli dopo aver ricevuto il formulario ma prima di concedere crediti. Se un segnale fallisce, blocca l'IP per una finestra configurabile e restituisci un errore generico.

Priorita 5: collegare l'impronta alla verifica email

Creare un account non equivale a provare di controllare la casella. Richiedi la verifica email prima che i crediti siano utilizzabili.

Il passo critico che la maggior parte dei team salta: l'impronta alla registrazione deve corrispondere all'impronta al clic sul link di verifica.

// Alla registrazione — memorizzare verifica in sospeso
await db.pendingVerifications.create({
  email,
  fingerprintSignup: fingerprint,
  ipSignup: clientIp,
  token: verificationToken,
  expiresAt: addHours(new Date(), 24),
});

// Alla verifica — confrontare impronta
const pending = await db.pendingVerifications.findByToken(token);

if (pending.fingerprintSignup !== currentFingerprint) {
  await flagAccount(pending.email, "fingerprint_mismatch");
  return { error: "Verifica fallita" };
}

await activateAccount(pending.email);

Una discrepanza non significa sempre frode — gli utenti cambiano dispositivo — ma discrepanze ripetute da IP ad alto rischio sono un segnale forte.

Priorita 6: smettere di regalare tutti i crediti alla registrazione

I crediti gratuiti sono il ROI dell'attaccante. Distribuisci valore su azioni difficili da automatizzare:

Milestone Crediti Perche
Account creato 0 Nessun valore immediato da estrarre
Email verificata +10 Dimostra controllo della casella
Onboarding completato +25 Richiede interazione reale col prodotto
Condivisione social confermata +15 Aggiunge frizione e traccia di audit

Esempio di flag nel back office:

interface UserCredits {
  emailVerified: boolean;      // +10 crediti
  onboardingCompleted: boolean; // +25 crediti
  sharedOnSocial: boolean;      // +15 crediti
}

function getAvailableCredits(user: UserCredits): number {
  let credits = 0;
  if (user.emailVerified) credits += 10;
  if (user.onboardingCompleted) credits += 25;
  if (user.sharedOnSocial) credits += 15;
  return credits;
}

I bot ottimizzano il percorso piu veloce verso i crediti. Un onboarding con chiamate API reali, scelte di configurazione o un breve tutorial interrompe i flussi scriptati.

Panoramica del flusso di registrazione

L'utente invia il formulario
        │
        ▼
Leggere CF-Connecting-IP
        │
        ▼
Verificare rate limit impronta + IP ──► bloccato? → 429, log
        │
        ▼
Creare account (0 crediti)
        │
        ├──► async: reputazione IP
        └──► async: validazione email
                    │
                    ▼
              minaccia rilevata? → bloccare IP, segnalare account
                    │
                    ▼
Inviare email di verifica (memorizzare impronta registrazione)
        │
        ▼
Clic sul link → impronta corrisponde? → +10 crediti
        │
        ▼
Onboarding completato → +25 crediti
        │
        ▼
Condivisione social confermata → +15 crediti

Boolean utili nel back office

Dai al tuo team toggle per reagire senza deploy:

  • block_disposable_emails — rifiutare caselle usa e getta alla registrazione
  • block_high_threat_ips — bloccare auto IP con threat_score >= 50
  • require_fingerprint_match — imporre corrispondenza impronta nella verifica email
  • rate_limit_by_fingerprint — attivare throttling per impronta
  • grant_credits_on_signup — mantenere a false durante un attacco attivo

Questi flag permettono di rafforzare le difese durante un incidente e allentarle quando il traffico si normalizza.

Ultima risorsa: rallentare senza avvisare

Se un IP e chiaramente abusivo ma vuoi evitare di allertare l'attaccante, alcuni team introducono frizione artificiale — risposte ritardate, CAPTCHA al secondo tentativo o un codice SMS falso che non arriva mai. E controverso, va riservato come ultima risorsa, chiaramente registrato internamente e mai mostrato agli utenti legittimi.

L'obiettivo e bruciare il tempo dell'attaccante, non il tuo.

Risultati

Dopo l'implementazione di questa stack — estrazione IP reale, controlli Veille IP ed email, rate limit FingerprintJS, verifica legata all'impronta e crediti progressivi — il volume di registrazioni abusive del cliente e calato nettamente entro 48 ore. Gli utenti legittimi hanno completato l'onboarding senza notare i controlli aggiuntivi.

Articoli correlati