WP Manifestindependent plugin directory
manifest / security / booking-captcha-gate

Booking Captcha Gate

Plugin de WordPress que entrega widgets de reserva solo tras verificar reCAPTCHA en el servidor

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/breixohr/booking-captcha-gate/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Plugin de WordPress que protege los widgets de reserva de terceros (Regiondo, FareHarbor, Bókun, Turitop…) frente a bots que hacen reservas falsas. El widget no se envía a la página: el servidor lo entrega solo después de verificar reCAPTCHA.

El problema

Los free tours (gratuitos, con reserva obligatoria) de varias webs de una agencia de turismo recibían ráfagas de reservas automatizadas: bots que llenaban el aforo de una sesión y dejaban fuera a los clientes reales. El calendario lo sirve un widget del proveedor de reservas, así que no se puede añadir un captcha dentro de su formulario.

La primera solución (v1) ponía una capa con reCAPTCHA encima del widget. Funcionaba para el uso normal, pero tenía un fallo de base: el widget ya estaba en el HTML de la página y el token de reCAPTCHA no se verificaba nunca en el servidor. Bastaba con borrar la capa desde DevTools o leer el HTML para saltársela.

Cómo funciona (v2)

sequenceDiagram
    participant B as Navegador
    participant WP as WordPress
    participant G as Google reCAPTCHA

    B->>WP: GET /free-tour-…
    WP-->>B: página con un marcador [gate: post 42, id a1b2…]<br/>(el widget NO está en el HTML)
    B->>G: el usuario resuelve el captcha (v2) o se puntúa (v3)
    G-->>B: token
    B->>WP: POST /wp-json/booking-captcha-gate/v1/unlock {post, gate, token}
    WP->>WP: límite por IP (10 / 10 min)
    WP->>G: siteverify(secret, token, ip)
    G-->>WP: success, hostname, score, action
    WP-->>B: 200 { html: widget }  ·  403 / 429 si no
    B->>B: inserta el widget y ejecuta sus scripts
[captcha_gate message="Confirma que no eres un robot para reservar"]
<div id="regiondo-widget" data-product="12345"></div>
<script src="https://widgets.regiondo.net/…"></script>
[/captcha_gate]

Decisiones de diseño

  • Verificación en el servidor (src/Verifier.php), con comprobación de:

    • el hostname, para que un token resuelto en otra web que use la misma clave no sirva aquí;
    • en v3, la action y la puntuación mínima.

    Ante cualquier fallo (red, HTTP, JSON inesperado, sin clave) el widget no se entrega.

  • Identificador estable por gate: es el hash del contenido y no su posición. WordPress puede renderizar el mismo contenido varias veces por petición (extractos, plugins de SEO) y un contador de posición se desincronizaba. El smoke test detectó este caso.

  • El contenido se vuelve a leer del post: el endpoint no acepta HTML del cliente, solo un id de post y un id de gate. No devuelve nada de borradores ni de posts protegidos con contraseña.

  • Límite de intentos por IP con transients (src/RateLimiter.php), y Cache-Control: no-store en la respuesta.

  • No rompe la venta: sin claves configuradas, el widget se muestra tal cual.

  • Clave secreta fuera de la base de datos: se puede definir con define('BOOKING_CAPTCHA_GATE_SECRET', '…') en wp-config.php.

  • Scripts del proveedor: innerHTML no ejecuta `