- 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>
342 lines
12 KiB
Markdown
342 lines
12 KiB
Markdown
# 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
|
|
|
|
### Formulario 1: Newsletter Footer
|
|
- **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)
|
|
|
|
```html
|
|
<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)
|
|
|
|
```html
|
|
<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>
|
|
```
|
|
|
|
### Nota sobre archivo Legacy (footer-contact.js)
|
|
|
|
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** | ✅ Sí | ✅ Sí |
|
|
| **Rate Limit** | ✅ 60s | ✅ 30s |
|
|
| **Sanitización** | ✅ Sí | ✅ Sí |
|
|
| **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
|
|
|
|
### PRIORIDAD 1: Newsletter Footer (Urgente)
|
|
|
|
#### 1.1 Honeypot Field (Implementación rápida, efectiva)
|
|
```html
|
|
<!-- 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`):
|
|
```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)
|
|
```php
|
|
// 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
|
|
```php
|
|
// 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
|
|
```php
|
|
// 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`
|
|
|
|
### Fase 2: Aplicar a Newsletter Footer
|
|
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
|
|
|
|
### Newsletter Footer
|
|
| 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.
|
|
|