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.
El problema
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.');
Por qué está escrito así
- 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.
Adaptalo a tu caso con IA
El botón copia este prompt con el código pegado debajo. Reemplazá los corchetes con lo tuyo y pegalo en ChatGPT, Claude, Gemini o 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.
Ejemplo escrito y revisado a mano.
Comentarios
Todavía no hay comentarios. El primero puede ser el tuyo.
¿Lo adaptaste a algo tuyo?
Contame qué resolviste con esto, o qué le faltó.