Proteger un formulario público sin sesión ni cookie
Un sello firmado con HMAC que vence solo, no se puede fabricar y de paso delata a los robots por lo rápido que responden.
The problem
Un formulario abierto al público recibe envíos automáticos. Abrir una sesión para cada visitante solo por eso es caro y deja cookies que nadie pidió.
<?php
const SEMILLA = 'cambiar-por-un-secreto-largo';
function sello(): string {
$ts = time();
return $ts . '.' . hash_hmac('sha256', 'form|' . $ts, SEMILLA);
}
function sello_edad(string $sello): ?int {
[$ts, $firma] = array_pad(explode('.', $sello, 2), 2, '');
$esperada = hash_hmac('sha256', 'form|' . (int) $ts, SEMILLA);
if (!hash_equals($esperada, $firma)) return null; // fabricado o alterado
$edad = time() - (int) $ts;
return ($edad < 0 || $edad > 21600) ? null : $edad; // vence a las 6 horas
}
// En el formulario: <input type="hidden" name="sello" value="<?= sello() ?>">
$edad = sello_edad($_POST['sello'] ?? '');
if ($edad === null) exit('Formulario vencido. Recargá la página.');
if ($edad < 3) exit('Demasiado rápido para ser una persona.');
Why it is written this way
- El sello es la hora en que se sirvió el formulario más su firma HMAC. Sin la semilla, nadie puede fabricar uno válido.
- hash_equals compara en tiempo constante: no filtra información por cuánto tarda la comparación.
- Como la hora viaja firmada, el servidor sabe cuánto tardó la persona en responder sin guardar nada.
- Un envío en menos de tres segundos casi nunca es humano: es el filtro contra robots más barato que existe.
Poné el campo oculto en tu formulario y el bloque final al principio del script que lo recibe. Enviá apenas carga la página: debe rechazarlo por rápido.
El mismo sello sirve para varios envíos durante seis horas: frena robots, no reemplaza un límite de envíos por IP. Y la semilla va en un archivo de configuración fuera del repositorio.
Adapt it to your case with AI
The button copies this prompt with the code pasted below it. Replace the brackets with your own details and paste it into ChatGPT, Claude, Gemini or Copilot.
Uso este sello HMAC en PHP para proteger un formulario público sin sesión. Agregale un campo trampa oculto (honeypot) y un límite de [CANTIDAD] envíos por IP por día guardado en [DÓNDE GUARDO: archivo, SQLite o MySQL]. Que los rechazos devuelvan mensajes distintos para una persona y silencio para un robot. PHP 8, sin frameworks.
Example written and reviewed by hand.
Comments
No comments yet. Yours can be the first.
Did you adapt it to something of yours?
Tell me what you solved with it, or what it was missing.