Files
roi-theme/_planificacion/analisis-spam-formularios.md
FrankZamora 0f6387ab46 refactor: reorganizar openspec y planificacion con spec recaptcha
- renombrar openspec/ a _openspec/ (carpeta auxiliar)
- mover specs de features a changes/
- crear specs base: arquitectura-limpia, estandares-codigo, nomenclatura
- migrar _planificacion/ con design-system y roi-theme-template
- agregar especificacion recaptcha anti-spam (proposal, tasks, spec)
- corregir rutas y referencias en todas las specs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 15:30:45 -06:00

12 KiB

Análisis de Spam en Formularios - ROI Theme

Fecha de análisis: 2026-01-08 Problema: Recepción de spam con datos aleatorios en formularios

Características del Spam Detectado

  • Nombres: Cadenas aleatorias (ej: kxUcwkDPHRAnUbdRWnDx, SOTbwKzTcZhJfTRBYSTV)
  • WhatsApp: Cadenas aleatorias en lugar de números
  • Emails: Emails reales (posiblemente robados de bases de datos)
  • Patrón: Bots automatizados que llenan campos sin validación
  • Fuente identificada: newsletter-footer

Módulos de Formularios Identificados

  • Handler: Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php
  • Renderer: Public/Footer/Infrastructure/Ui/FooterRenderer.php
  • Acción AJAX: roi_newsletter_subscribe
  • Source en payload: newsletter-footer

Formulario 2: Contact Form (Sección + Modal)

  • Handler: Public/ContactForm/Infrastructure/Api/WordPress/ContactFormAjaxHandler.php
  • Renderer: Public/ContactForm/Infrastructure/Ui/ContactFormRenderer.php
  • Acción AJAX: roi_contact_form_submit
  • Source en payload: contact-form

Hallazgos Durante la Revisión

Archivos Identificados

Schemas:

  • Schemas/contact-form.json

JavaScript (Frontend):

  • Assets/Js/footer-contact.js
  • Assets/Js/modal-contact.js

PHP Backend - Newsletter (FUENTE DEL SPAM):

  • Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php
  • Public/Footer/Infrastructure/Ui/FooterRenderer.php

Análisis del Formulario Newsletter (PRINCIPAL AFECTADO)

Ubicación

  • Handler AJAX: Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php
  • Renderer HTML/JS: Public/Footer/Infrastructure/Ui/FooterRenderer.php
  • Fuente en payload: newsletter-footer ✓ (coincide con spam reportado)

Medidas de Seguridad EXISTENTES

Medida Estado Archivo Línea
Nonce WordPress Implementado NewsletterAjaxHandler.php 46
Rate Limiting (60s por IP) Implementado NewsletterAjaxHandler.php 54-59
Sanitización de inputs Implementado NewsletterAjaxHandler.php 62-64
Validación de email Implementado NewsletterAjaxHandler.php 66
Webhook URL oculta Implementado Nunca expuesta al cliente

VULNERABILIDADES CRÍTICAS

Vulnerabilidad Impacto Prioridad
NO hay honeypot Bots pasan sin detección 🔴 ALTA
NO hay CAPTCHA Sin verificación humana 🔴 ALTA
NO hay validación de nombre Acepta kxUcwkDPHRAnUbdRWnDx 🔴 ALTA
NO hay validación de WhatsApp Acepta OGkrLENXqiQAaIYvCV 🔴 ALTA
Rate limiting débil 60s es poco, bots rotan IPs 🟡 MEDIA
NO hay tiempo mínimo de envío Bots envían instantáneamente 🟡 MEDIA

Campos del Formulario (FooterRenderer.php:336-343)

<form id="roi-newsletter-form">
  <input type="hidden" name="nonce" value="...">
  <input type="text" name="name" placeholder="Nombre">           <!-- SIN VALIDACIÓN -->
  <input type="email" name="email" placeholder="Email" required> <!-- SOLO HTML5 -->
  <input type="tel" name="whatsapp" placeholder="WhatsApp">      <!-- SIN VALIDACIÓN -->
  <button type="submit">Suscribirse</button>
</form>

Patrón del Spam Detectado

Los datos de spam muestran:

  • Nombres: Cadenas alfanuméricas aleatorias (20+ caracteres)
  • WhatsApp: Cadenas alfanuméricas aleatorias (NO son números)
  • Emails: Emails reales (posiblemente de bases de datos filtradas)

Esto indica bots automatizados que:

  1. Bypassean la validación HTML5 (trivial)
  2. Ignoran el rate limiting rotando IPs
  3. Generan valores aleatorios para campos de texto
  4. Usan emails reales para parecer legítimos

Análisis del Formulario Contact Form (SEGUNDO FORMULARIO)

Ubicación

  • Handler AJAX: Public/ContactForm/Infrastructure/Api/WordPress/ContactFormAjaxHandler.php
  • Renderer: Public/ContactForm/Infrastructure/Ui/ContactFormRenderer.php
  • Schema: Schemas/contact-form.json
  • Fuente en payload: contact-form

Estado: ACTIVO (Clean Architecture)

Este formulario está implementado correctamente con Clean Architecture:

  • Renderer genera HTML/CSS/JS dinámicamente
  • Handler AJAX procesa envíos server-side
  • Webhook URL guardada en BD (nunca expuesta al cliente)

Medidas de Seguridad EXISTENTES

Medida Estado Archivo Línea
Nonce WordPress Implementado ContactFormAjaxHandler.php 47-48
Rate Limiting (30s por IP) Implementado ContactFormAjaxHandler.php 55-61
Sanitización de inputs Implementado ContactFormAjaxHandler.php 122-130
Validación de email Implementado ContactFormAjaxHandler.php 151-155
Webhook URL oculta Implementado ContactFormAjaxHandler.php 86

VULNERABILIDADES CRÍTICAS

Vulnerabilidad Impacto Prioridad
NO hay honeypot Bots pasan sin detección 🔴 ALTA
NO hay CAPTCHA Sin verificación humana 🔴 ALTA
NO hay validación de nombre Acepta kxUcwkDPHRAnUbdRWnDx 🔴 ALTA
NO hay validación de WhatsApp Acepta OGkrLENXqiQAaIYvCV 🔴 ALTA
Rate limiting muy débil Solo 30s, bots rotan IPs 🟡 MEDIA
NO hay tiempo mínimo de envío Bots envían instantáneamente 🟡 MEDIA

Campos del Formulario (ContactFormRenderer.php:278-320)

<form id="roiContactForm" data-nonce="...">
  <input type="text" name="fullName" placeholder="Nombre completo *" required>  <!-- SIN VALIDACIÓN FORMATO -->
  <input type="text" name="company" placeholder="Empresa">
  <input type="tel" name="whatsapp" placeholder="WhatsApp *" required>          <!-- SIN VALIDACIÓN FORMATO -->
  <input type="email" name="email" placeholder="Correo electrónico *" required> <!-- SOLO required + is_email -->
  <textarea name="message"></textarea>
</form>

El archivo Assets/Js/footer-contact.js es código legacy NO utilizado:

  • NO está encolado en Inc/enqueue-scripts.php
  • Tiene webhook URL hardcodeada (mala práctica)
  • Fue reemplazado por el sistema Clean Architecture actual

Resumen Comparativo de Ambos Formularios

Característica Newsletter Footer Contact Form
Estado ACTIVO ACTIVO
Backend PHP AJAX PHP AJAX
Nonce
Rate Limit 60s 30s
Sanitización
Validación email is_email() is_email()
Validación nombre Ninguna Solo required
Validación WhatsApp Ninguna Solo required
Honeypot NO NO
CAPTCHA NO NO
Tiempo mínimo NO NO

Conclusión: AMBOS formularios comparten las MISMAS vulnerabilidades críticas


Soluciones Anti-Spam Propuestas

1.1 Honeypot Field (Implementación rápida, efectiva)

<!-- Campo oculto que humanos no llenan pero bots sí -->
<input type="text" name="website_url" style="display:none !important" tabindex="-1" autocomplete="off">

Backend (NewsletterAjaxHandler.php):

// Verificar honeypot
$honeypot = sanitize_text_field($_POST['website_url'] ?? '');
if (!empty($honeypot)) {
    // Es bot - responder éxito falso para no alertar
    wp_send_json_success(['message' => $successMsg]);
    return;
}

1.2 Validación de Contenido (Anti-gibberish)

// Validar nombre: solo letras, espacios, acentos
$name = sanitize_text_field($_POST['name'] ?? '');
if (!empty($name) && !preg_match('/^[\p{L}\s\'-]{2,50}$/u', $name)) {
    wp_send_json_error(['message' => 'Nombre inválido'], 422);
    return;
}

// Validar WhatsApp: solo números, +, -, espacios
$whatsapp = sanitize_text_field($_POST['whatsapp'] ?? '');
if (!empty($whatsapp) && !preg_match('/^[\d\s\+\-\(\)]{10,20}$/', $whatsapp)) {
    wp_send_json_error(['message' => 'Número de WhatsApp inválido'], 422);
    return;
}

1.3 Tiempo Mínimo de Envío

// En el formulario HTML agregar:
<input type="hidden" name="form_timestamp" value="<?php echo time(); ?>">

// En el handler:
$timestamp = (int) ($_POST['form_timestamp'] ?? 0);
$minTime = 3; // segundos mínimos
if (time() - $timestamp < $minTime) {
    // Envío demasiado rápido = bot
    wp_send_json_success(['message' => $successMsg]); // Falso éxito
    return;
}

1.4 Rate Limiting Mejorado

// Aumentar de 60s a 300s (5 minutos)
// Y agregar límite diario por IP
private function checkRateLimit(): bool
{
    $ip = $this->getClientIP();

    // Límite corto (5 minutos)
    $shortKey = 'roi_newsletter_short_' . md5($ip);
    if (get_transient($shortKey) !== false) {
        return false;
    }
    set_transient($shortKey, time(), 300);

    // Límite diario (máximo 5 por día)
    $dailyKey = 'roi_newsletter_daily_' . md5($ip);
    $dailyCount = (int) get_transient($dailyKey);
    if ($dailyCount >= 5) {
        return false;
    }
    set_transient($dailyKey, $dailyCount + 1, DAY_IN_SECONDS);

    return true;
}

PRIORIDAD 2: Opciones Adicionales

2.1 Google reCAPTCHA v3 (Invisible)

  • Sin fricción para usuarios
  • Score basado en comportamiento
  • Requiere cuenta Google reCAPTCHA

2.2 Cloudflare Turnstile (Recomendado)

  • Gratuito
  • Sin tracking de Google
  • Más privado que reCAPTCHA

2.3 hCaptcha

  • Alternativa a reCAPTCHA
  • Mejor privacidad
  • Versión gratuita disponible

Plan de Implementación

Fase 1: Crear Servicio Anti-Spam Compartido

  1. Crear Shared/Infrastructure/Services/AntiSpamValidator.php
  2. Implementar validaciones reutilizables:
    • validateHoneypot(string $value): bool
    • validateMinimumTime(int $timestamp, int $minSeconds = 3): bool
    • validateNameFormat(string $name): bool (regex letras/espacios)
    • validateWhatsAppFormat(string $phone): bool (regex números)
    • checkEnhancedRateLimit(string $ip, int $shortLimit, int $dailyLimit): bool
  1. Agregar honeypot + timestamp en FooterRenderer.php
  2. Integrar AntiSpamValidator en NewsletterAjaxHandler.php
  3. Probar que spam es rechazado

Fase 3: Aplicar a Contact Form

  1. Agregar honeypot + timestamp en ContactFormRenderer.php
  2. Integrar AntiSpamValidator en ContactFormAjaxHandler.php
  3. Probar que spam es rechazado

Fase 4: Monitoreo y Mejoras (Opcional)

  1. Agregar logging de intentos sospechosos
  2. Evaluar CAPTCHA invisible si spam persiste
  3. Dashboard de estadísticas anti-spam

Archivos a Modificar

Archivo Cambio
Public/Footer/Infrastructure/Ui/FooterRenderer.php Agregar honeypot + timestamp oculto
Public/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.php Validaciones formato + honeypot check + rate limiting mejorado

Contact Form

Archivo Cambio
Public/ContactForm/Infrastructure/Ui/ContactFormRenderer.php Agregar honeypot + timestamp oculto
Public/ContactForm/Infrastructure/Api/WordPress/ContactFormAjaxHandler.php Validaciones formato + honeypot check + rate limiting mejorado

Código Reutilizable (Recomendado)

Crear un trait o clase helper compartida para evitar duplicación:

Shared/Infrastructure/Services/AntiSpamValidator.php
  • Validación de honeypot
  • Validación de tiempo mínimo
  • Validación de formato nombre (regex)
  • Validación de formato WhatsApp (regex)
  • Rate limiting mejorado

Referencias Serena

Las memorias existentes en .serena/Memories/ documentan:

  • diagnostico-estructura-carpetas-admin.md - Estructura de carpetas Admin
  • migracion-theme-options-tabla-personalizada.md - Sistema de settings
  • ROI_THEME_CSS_LOADING_ANALYSIS.md - Análisis de carga CSS

Esta memoria debe guardarse como referencia para implementación futura.