BrunoP.Blog

Como evitar spam em formulários sem CAPTCHA (Honeypot e Time-trap)

O reCAPTCHA ajuda a barrar bots, mas destrói a conversão e incomoda o usuário real. Te mostro como proteger seus formulários usando o método Honeypot (pote de mel) e Time-trap, com código limpo de copiar e colar para o seu site.

Ninguém gosta de resolver quebra-cabeças visuais para enviar um simples formulário de contato. O reCAPTCHA tradicional da Google (e alternativas similares) criam fricção no momento mais crítico do seu funil: a conversão do usuário. Estudos mostram que testes de CAPTCHA complexos podem derrubar as taxas de conversão de leads em até 3% a 5%.

A alternativa inteligente é a segurança invisível. Ao invés de forçar o humano a provar que não é um robô, nós fazemos com que o robô revele sua própria identidade de forma silenciosa. Duas técnicas clássicas e muito eficazes para isso são o Honeypot e o Time-trap.

Como funciona o Honeypot (Pote de Mel)

Bots de spam automatizados vasculham a web lendo o código HTML bruto das páginas. Ao encontrar uma tag <form>, eles tentam preencher todos os campos de texto disponíveis para maximizar a chance de a mensagem passar, ignorando estilos visuais.

O Honeypot consiste em adicionar um campo de formulário adicional que fica totalmente invisível para os humanos através de CSS, mas permanece visível no código para os bots. Se o formulário for submetido e esse campo oculto contiver qualquer dado, sabemos com absoluta certeza que foi preenchido por um robô, e a requisição é descartada.

Dica de ouro: Não use nomes óbvios como honeypot, spam ou hidden_field no atributo name. Bots avançados evitam campos assim. Use nomes realistas que simulem informações normais, como website, phone_extension ou address_line2, e configure tabindex="-1" para evitar que usuários navegando via teclado parem no campo.

Como funciona o Time-trap (Armadilha de Tempo)

Robôs são projetados para preencher e enviar formulários o mais rápido possível, muitas vezes em frações de segundo após a requisição inicial. Humanos legítimos levam tempo para ler a página, posicionar o cursor, digitar o nome, e-mail e escrever a mensagem — um processo que raramente leva menos de 3 a 5 segundos.

O Time-trap registra o timestamp exato do momento em que a página foi carregada (geralmente salva na sessão do PHP ou criptografada num token). Quando os dados chegam ao servidor, verificamos se a diferença de tempo é menor do que esse limite mínimo (ex: 3 segundos). Se for rápido demais, o envio é bloqueado.

Aviso crucial: Trate como a PRIMEIRA CAMADA

Embora o Honeypot e o Time-trap eliminem cerca de 95% do ruído causado por scripts básicos, eles não constituem uma solução de segurança completa. Bots sofisticados rodando em navegadores automatizados (como Puppeteer ou Playwright) conseguem simular movimentos de mouse, ignorar campos invisíveis e introduzir pausas artificiais antes do envio.

Por isso, você deve parear essas técnicas com boas práticas robustas:

  • Validação e Sanitização Server-Side: Nunca confie na validação de e-mail do navegador. Valide formatos no PHP e limpe strings.
  • Escape toda saída: Se você renderizar os dados do formulário de volta (num log de erros ou painel administrativo), filtre as strings com htmlspecialchars() para evitar ataques de XSS (Cross-Site Scripting). Nunca use echo em inputs brutos.
  • Rate Limiting (Limite de Requisições): Evite que um atacante bombardeie seu servidor de e-mail (gerando centenas de e-mails em poucos segundos) limitando o número de submissões permitidas por endereço IP por minuto.

Teste as duas técnicas na prática no simulador interativo abaixo.

Simulator: Honeypot & Time-trap

Honeypot Field Vazio / Empty
Time-trap 0.00s
PHP backend console

[Sistema] Servidor pronto para validação local.

[Sistema] Aguardando envio em modo Humano...

Código Pronto: Cole no seu site

<!-- Formulário HTML -->
<form action="processa.php" method="POST">
  <!-- Campo de time-trap dinâmico (carregado via PHP) -->
  <input type="hidden" name="form_token" value="<?= time(); ?>">

  <div class="campo-grupo">
    <label for="nome">Nome</label>
    <input type="text" id="nome" name="nome" required>
  </div>

  <div class="campo-grupo">
    <label for="email">E-mail</label>
    <input type="email" id="email" name="email" required>
  </div>

  <!-- CAMPO HONEYPOT (Oculto via CSS de forma orgânica) -->
  <div class="campo-oculto-protecao">
    <label for="website">Website</label>
    <input type="text" id="website" name="website" tabindex="-1" autocomplete="off">
  </div>

  <div class="campo-grupo">
    <label for="mensagem">Mensagem</label>
    <textarea id="mensagem" name="mensagem" required></textarea>
  </div>

  <button type="submit">Enviar</button>
</form>

<style>
/* Esconde o Honeypot sem usar "display: none" */
.campo-oculto-protecao {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
  opacity: 0;
  pointer-events: none;
}
</style>
<?php
/**
 * processa.php - Validação de segurança no Backend
 */
session_start();

// 1. Validação de Time-trap (Mínimo de 3 segundos para digitação)
$tempo_minimo = 3;
$token_carregamento = filter_input(INPUT_POST, 'form_token', FILTER_VALIDATE_INT);

if (!$token_carregamento || (time() - $token_carregamento) < $tempo_minimo) {
    // Rejeita a submissão rápida demais
    http_response_code(400);
    exit("Erro: Envio bloqueado por validação de tempo.");
}

// 2. Validação de Honeypot (O campo oculto "website" deve estar vazio)
$honeypot = filter_input(INPUT_POST, 'website', FILTER_DEFAULT);

if (!empty($honeypot)) {
    // Rejeita se o bot preencheu o campo invisível
    http_response_code(400);
    exit("Erro: Spam detectado.");
}

// 3. Sanitização e Validação do restante dos inputs
$nome = filter_input(INPUT_POST, 'nome', FILTER_DEFAULT);
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$mensagem = filter_input(INPUT_POST, 'mensagem', FILTER_DEFAULT);

if (!$email) {
    http_response_code(400);
    exit("Erro: E-mail inválido.");
}

// 4. PRIMEIRA CAMADA CONCLUÍDA - Preparação segura dos dados
// Escapa toda e qualquer saída em tela ou relatórios para prevenir XSS
$nome_seguro = htmlspecialchars($nome ?? '', ENT_QUOTES, 'UTF-8');
$mensagem_segura = htmlspecialchars($mensagem ?? '', ENT_QUOTES, 'UTF-8');

// IMPORTANTE: Combine esta camada com validações server-side robustas,
// verificação de CSRF tokens e regras de rate limiting por IP para 
// blindar totalmente o processamento de envios contra ataques distribuídos.

echo "Sucesso! Mensagem de " . $nome_seguro . " processada de forma segura.";

Quero blindar a segurança dos meus formulários e site

Perguntas frequentes

O Honeypot e o Time-trap bloqueiam todos os spams? Não. Eles filtram a maioria dos spams automatizados mais comuns. Bots mais avançados (que rodam navegadores inteiros) requerem defesas no backend, checagem de IP e limites de envio (rate limit). Os métodos descritos aqui são a primeira camada de defesa.
Esconder o Honeypot com display: none funciona? Alguns bots mais inteligentes evitam campos marcados com display: none por CSS. A melhor tática é usar posicionamento absoluto negativo ou opacidade zero no campo.
Como garantir que o PHP não execute códigos maliciosos? Sempre filtre as variáveis recebidas (ex: via filter_input) e use htmlspecialchars() em qualquer saída. Nunca dê echo em dados brutos enviados pelo usuário.

Este guia faz parte da série Segurança de sites. Referências: OWASP — Anti-Spam, MDN — HTML Inputs.