Back to code PHP

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.

This post is not in your language yet; you are seeing it in Spanish. The code is the same.

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
<?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

  1. 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.
  2. hash_equals compara en tiempo constante: no filtra información por cuánto tarda la comparación.
  3. Como la hora viaja firmada, el servidor sabe cuánto tardó la persona en responder sin guardar nada.
  4. Un envío en menos de tres segundos casi nunca es humano: es el filtro contra robots más barato que existe.
How to try it

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.

Watch out

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.

The prompt
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.

Share

Comments

No comments yet. Yours can be the first.

Plain text only, no links. No email is requested. Comments are reviewed before they are published.

Did you adapt it to something of yours?

Tell me what you solved with it, or what it was missing.

Keep trying

More code

See all
Darinel Ortega Online · I reply during the day
Today
Hello. I'm not selling anything here: this is for exchanging knowledge about technology.
Write whatever you like — you can send text, images or documents. Messages reach my console and I reply from there.
You are writing outside working hours. I will read you as soon as the day starts.

An open conversation to share knowledge. Messages reach my console and I reply from there.

Let us book a conversation

Pick the day and time that work for you. Thirty minutes, no sales pitch.

Video call

For a video call, just ask for one here and I'll send you the session link.