Voltar ao blog
Seguranca

Guia pratico para proteger cadastros SaaS B2B

Josselin Liebe
Josselin Liebe

No mes passado, um cliente SaaS B2B veio ate nos com um problema familiar: os cadastros pareciam normais na superficie, mas os creditos gratuitos sumiam rapido. Ao analisar os logs, descobrimos um ataque coordenado — dezenas de registros dos mesmos sub-redes /24, emails descartaveis em rotacao e um atacante reutilizando infraestrutura em multiplas contas falsas.

Este artigo percorre a stack de defesa que recomendamos. Nomes e identificadores estao anonimizados, mas o fluxo esta pronto para producao.

O que facilitou o ataque

Tres fatores jogaram a favor do atacante:

  1. Todos os creditos gratuitos concedidos no cadastro — sem friccao, sem verificacao, valor imediato.
  2. Sem vinculacao de dispositivo — a mesma impressao digital do navegador podia se cadastrar repetidamente.
  3. Verificacoes de IP ausentes ou mal configuradas — atras do proxy Cloudflare (nuvem laranja), o app lia IPs de proxy em vez do endereco real do visitante.

Proteger cadastros nao e uma bala de prata — e empilhar sinais que se reforcam mutuamente.

Prioridade 1: ler o IP real

Se seu app esta atras do Cloudflare com proxy ativado (nuvem laranja), req.ip ou X-Forwarded-For sozinhos nao bastam. O Cloudflare envia o IP real do visitante no 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";
}

Cada verificacao posterior — rate limits, reputacao de IP, bloqueio — depende de acertar este primeiro passo.

Prioridade 2: pontuar o IP no cadastro

Execute a API Veille IP Reputation de forma assincrona durante o cadastro. Se possivel, nao bloqueie a resposta HTTP nela; enfileire a verificacao e aja em segundos.

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();
}

O threat_score agrega os sinais detectados:

Score Significado
0 IP residencial ou empresarial limpa
1–25 Sinais menores (datacenter, VPN)
25–50 Risco moderado (multiplos sinais)
50+ Risco alto (Tor, abusador conhecido, proxy) — bloquear ou exigir verificacao extra

Nos logs do nosso cliente, IPs atacantes se agrupavam em faixas /24 com scores acima de 50. Bloquear a sub-rede apos abusos repetidos, nao apenas o IP isolado, reduziu o volume rapidamente.

Prioridade 3: integrar FingerprintJS

Instale FingerprintJS nas paginas de cadastro e login. Armazene o visitorId em cada tentativa.

import FingerprintJS from "@fingerprintjs/fingerprintjs";

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

Depois aplique rate limits tanto em IP quanto em impressao digital:

  • Apos 5 tentativas falhas de cadastro ou login do mesmo fingerprint ou IP → bloqueio por 24 horas.
  • Registre ambos os valores em cada tentativa para pivotar facilmente no back office.

Prioridade 4: validar o email de forma assincrona

Emails descartaveis nao param um atacante determinado, mas filtram grande parte do abuso de baixo esforco. Chame a API Veille Email Validation em paralelo com a verificacao de 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();
}

// No handler de cadastro:
const [ipResult, emailResult] = await Promise.all([
  scoreIp(clientIp),
  validateEmail(email),
]);

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

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

Execute essas verificacoes depois de receber o formulario mas antes de conceder creditos. Se algum sinal falhar, bloqueie o IP por uma janela configuravel e retorne um erro generico.

Prioridade 5: vincular fingerprint a verificacao de email

Criar uma conta nao e o mesmo que provar que voce controla a caixa de entrada. Exija verificacao de email antes que os creditos sejam utilizaveis.

A etapa critica que a maioria das equipes omite: o fingerprint no cadastro deve corresponder ao fingerprint ao clicar no link de verificacao.

// No cadastro — armazenar verificacao pendente
await db.pendingVerifications.create({
  email,
  fingerprintSignup: fingerprint,
  ipSignup: clientIp,
  token: verificationToken,
  expiresAt: addHours(new Date(), 24),
});

// Na verificacao — comparar fingerprint
const pending = await db.pendingVerifications.findByToken(token);

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

await activateAccount(pending.email);

Uma divergencia nem sempre significa fraude — usuarios trocam de dispositivo — mas divergencias repetidas de IPs de alto risco sao um sinal forte.

Prioridade 6: parar de dar todos os creditos no cadastro

Creditos gratuitos sao o ROI do atacante. Distribua valor em acoes dificeis de automatizar:

Marco Creditos Por que
Conta criada 0 Sem valor imediato a extrair
Email verificado +10 Prova controle da caixa de entrada
Onboarding concluido +25 Exige interacao real com o produto
Compartilhamento social confirmado +15 Adiciona friccao e trilha de auditoria

Exemplo de flags no back office:

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

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;
}

Bots otimizam o caminho mais rapido para creditos. Um onboarding com chamadas API reais, escolhas de configuracao ou um tutorial curto quebra fluxos scriptados.

Visao geral do fluxo de cadastro

Usuario envia formulario
        │
        ▼
Ler CF-Connecting-IP
        │
        ▼
Verificar rate limits fingerprint + IP ──► bloqueado? → 429, log
        │
        ▼
Criar conta (0 creditos)
        │
        ├──► async: reputacao IP
        └──► async: validacao email
                    │
                    ▼
              ameaca detectada? → bloquear IP, marcar conta
                    │
                    ▼
Enviar email de verificacao (armazenar fingerprint do cadastro)
        │
        ▼
Clique no link → fingerprint coincide? → +10 creditos
        │
        ▼
Onboarding concluido → +25 creditos
        │
        ▼
Compartilhamento social confirmado → +15 creditos

Booleanos uteis no back office

De a sua equipe toggles para reagir sem deploy:

  • block_disposable_emails — rejeitar caixas descartaveis no cadastro
  • block_high_threat_ips — bloquear auto IPs com threat_score >= 50
  • require_fingerprint_match — exigir correspondencia de fingerprint na verificacao de email
  • rate_limit_by_fingerprint — ativar throttling por fingerprint
  • grant_credits_on_signup — manter em false durante ataque ativo

Esses flags permitem reforcar defesas durante um incidente e relaxa-las quando o trafego normaliza.

Ultimo recurso: desacelerar sem alertar

Se um IP e claramente abusivo mas voce quer evitar alertar o atacante, algumas equipes introduzem friccao artificial — respostas atrasadas, CAPTCHA na segunda tentativa ou um codigo SMS falso que nunca chega. E controverso, deve ser ultimo recurso, claramente registrado internamente e nunca mostrado a usuarios legitimos.

O objetivo e queimar o tempo do atacante, nao o seu.

Resultados

Apos implementar esta stack — extracao IP real, verificacoes Veille IP e email, rate limits FingerprintJS, verificacao vinculada ao fingerprint e creditos progressivos — o volume de cadastros abusivos do cliente caiu bruscamente em 48 horas. Usuarios legitimos concluiram o onboarding sem notar os controles adicionais.

Artigos relacionados