¿Cuánto riesgo supone exponer la REST API de WordPress en un proyecto headless? Auditorías y ataques reales muestran que endpoints abiertos facilitan scraping masivo, enumeración de usuarios, CSRF y robo de tokens; previews mal protegidos filtran contenido privado y permiten escalada de permisos si no se controla el acceso.
Si usas WordPress headless o expones la REST API, reduce la superficie de ataque con autenticación robusta (JWT/OAuth2), limita endpoints, aplica rate‑limit, CORS y WAF, y protege previews. Incluye comandos nginx/htaccess, snippets para tokens, reglas WAF, pruebas automatizadas y un checklist operativo para desplegar una API segura y estable en producción.
Estos plazos son orientativos: la implementación puede completarse en 1–3 días para proyectos pequeños o cambios puntuales, pero en instalaciones con integraciones complejas o requisitos de compliance es frecuente necesitar más tiempo para pruebas funcionales, escaneos (WPScan, OWASP ZAP) y ajustes de rendimiento; planifique ventanas de staging y al menos 48–72 horas de verificación de rendimiento y seguridad antes de promover a producción.
Resumen del proceso en 7 pasos
La guía ofrece un plan práctico y ejecutable para asegurar una API REST en un proyecto headless en 1–3 días por release.
- Reducir superficie: identificar y deshabilitar endpoints no usados.
- Autenticación: implementar JWT con refresh rotatorio o OAuth2 Authorization Code.
- Controles de servidor: nginx/htaccess, rate‑limit y WAF en el borde.
- CORS y CSRF: configurar orígenes permitidos y cookies HttpOnly.
- Pruebas y monitorización: WPScan, OWASP ZAP, logs y alertas p95/p99.
- Previews seguros: signed cookies y permisos mínimos para editor.
- Checklist operativo: tests, backups, responsables y thresholds.
La lista anterior ofrece un plan de despliegue mínimo y ejemplos orientativos, pero algunos controles (por ejemplo reglas completas de WAF, .htaccess y los flujos front/back de tokens) requieren adaptar los snippets a su infraestructura y validar en staging antes de promoverlos a producción. A continuación se incluyen ejemplos reproducibles y comandos de verificación que facilitan esa validación.
Paso 1: reducir la superficie y bloquear endpoints
Primera tarea: identificar los endpoints públicos y desactivar los que no se usan.
¿Qué endpoints bloquear primero?
Bloquee /wp/v2/users, /wp/v2/settings y rutas administrativas que no necesite exponer.
Un caso habitual: proyecto headless que expone /wp/v2/users → atacantes enumeran usuarios y prueban fuerza bruta contra el login.
¿Cómo deshabilitar endpoints sin romper el sitio
Usar un filtro PHP para eliminar endpoints y probar en staging antes del deploy a producción.
Código mínimo para functions.php:
php
add_filter('rest_endpoints', function($endpoints){
unset($endpoints['/wp/v2/users']);
unset($endpoints['/wp/v2/settings']);
return $endpoints;
});
Flujo de reducción de superficie
InventarioListar rutas /wp-json y webhooks
Bloqueo temporalnginx 403 a rutas sensibles
AutenticaciónJWT / OAuth2 y refresh seguro
WAF y Rate‑limitEdge rules y límites por IP
PruebasWPScan, ZAP y pentest
Un threat model claro ayuda a priorizar mitigaciones. Parta de tres activos: contenido privado (previews), credenciales/tokens y estabilidad del servicio. Identifique actores (scrapers automatizados, atacantes con listas de usuarios, insiders) y vectores típicos: scraping masivo (alto volumen de GET a /wp-json), enumeración de usuarios (/wp/v2/users o author archives), CSRF en endpoints que aceptan cookies, token theft por XSS o redes inseguras, y abuso de previews que devuelven contenido sin autorización. Para cada vector conviene asignar likelihood y impacto y aplicar controles concretos: rate‑limit y challenges para scraping, deshabilitar o allow‑list endpoints sensibles para enumeración de usuarios, SameSite+HttpOnly y CSRF tokens para formularios que usan cookies, rotación y revocación de refresh tokens para limitar el impacto de token theft, y signed cookies con expiración corta y verificación HMAC para previews.
Complementar con detección (anomalías por IP, reconocimiento de user‑agent, fingerprinting) y playbooks de respuesta permite convertir la matriz de amenazas en acciones operativas medibles.
Paso 2: autenticación robusta. JWT y OAuth2 bien hechos
La recomendación: access tokens cortos y refresh tokens rotatorios con revocación server‑side.
¿Por qué JWT sin refresh es peligroso?
El error más frecuente en este punto es usar JWT de larga vida sin mecanismo de revocación.
Si un token dura horas, un atacante con acceso lo usa hasta que expira; por eso los access tokens deben durar minutos.
Flujo recomendado con JWT
Emitir un access token de 5–15 minutos y un refresh token con almacenamiento seguro y rotación.
Ejemplo de emisión y cookie secure:
php // Simplificado:
- emitir JWT y setear refresh cookie segura $access = generate_jwt($user_id, $exp = time()+900)
- $refresh = bin2hex(random_bytes(32))
- // Guardar refresh token en DB con user_id, jti y expiración setcookie('refresh', $refresh, ['httponly'=>true,'samesite'=>'Strict','secure'=>true])
Implementar revocación y rotación
Registrar cada refresh token con jti y fecha; al usar uno, invalidar el anterior.
Esto reduce el riesgo de token theft y permite revocar acceso sin forzar logout masivo.
Un flujo end‑to‑end claro reduce errores de implementación. Ejemplo mínimo recomendado:
- POST /auth/login devuelve un access_token (JWT, 5–15 minutos) en el body y setea una cookie HttpOnly 'refresh' rotatoria
- POST /auth/refresh lee la cookie HttpOnly, valida el refresh token en la base de datos por jti, emite un nuevo access_token y genera un nuevo refresh token rotatorio invalidando el anterior (guardar jti/revocado)
- POST /auth/logout borra el refresh token server‑side y expira la cookie. Ejemplo básico de refresh con curl:
curl -i -X POST https://api.midominio.es/auth/refresh --cookie "refresh="
Para OAuth2 Authorization Code con PKCE en SPA: el cliente inicia el flujo generando code_verifier/code_challenge, redirige al Authorization Server; una vez recibido el authorization code el backend del cliente intercambia code+verifier por tokens y guarda el refresh token de forma segura server‑side (no en localStorage). La combinación de refresh tokens rotatorios, revocación por jti y almacenamiento server‑side reduce el riesgo de token theft y permite revocar sesiones sin logout global.
Paso 3: controles en el servidor: nginx, htaccess y WAF
Colocar controles antes de que PHP procese la petición reduce carga y riesgo.
¿Qué reglas nginx aplicar para APIs?
Limitar tasa, bloquear rutas y forzar HTTPS con HSTS.
Ejemplos nginx:
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
- server { listen 443 ssl
- add_header Strict-Transport-Security "max-age=63072000
- includeSubDomains
- preload"
- location /wp-json/ { limit_req zone=api burst=20 nodelay
- proxy_pass http://backend
- } location ~ ^/wp-json/(wp/v2/users|wp/v2/settings) { return 403
- } }
Reglas ModSecurity y WAF gestionado
Instalar reglas gestionadas de ModSecurity y añadir firmas para JSON anómalo y patrones de scraping.
Ejemplo conceptual ModSecurity:
SecRule REQUEST_BODY "@regex select/s+.*from" "id:1001,deny,log,msg:'SQLi detected'"
Consideraciones de rendimiento
Esto funciona bien en teoría, pero en la práctica las reglas WAF aumentan latencia; medir p95/p99 y activar caché por compuerta si es necesario.
Para equipos operativos es útil contar con snippets reproducibles que se puedan copiar y ejecutar en staging. Por ejemplo, para Apache/htaccess se puede bloquear ruta concreta y añadir cabeceras HSTS y CORS desde el propio vHost o .htaccess:
RewriteEngine On
RewriteRule ^wp-json/wp/v2/users - [F,L]
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header set Access-Control-Allow-Origin "https://app.midominio.es"
Y en ModSecurity se pueden añadir firmas prácticas para detectar scraping y patrones JSON anómalos, por ejemplo una regla simple que bloquee body con demasiadas repeticiones o intentos de inyección:
SecRule REQUEST_HEADERS:Content-Type "application/json" "id:100100,phase:2,chain,deny,log,msg:'JSON anomalous or large'",
SecRule TX:ANOMALY_SCORE "@gt 5"
Para verificar reglas en entorno real use curl con cabeceras y cookies: curl -X GET -H 'Origin: https://app.midominio.es' -I https://api.midominio.es/wp-json/wp/v2/posts y pruebe respuestas 403/429 para rutas protegidas. Estos ejemplos (.htaccess, ModSecurity y curl) permiten ejecutar cambios mínimos y validar su efecto antes de promoverlos a producción.
Paso 4: CORS, CSRF y almacenamiento seguro de tokens
Configurar CORS restrictivo y usar cookies HttpOnly para refresh tokens reduce el riesgo de CSRF y token theft.
Configuración CORS segura
Permitir solo orígenes de frontend y activar credentials cuando se usan cookies HttpOnly.
Ejemplo nginx:
add_header 'Access-Control-Allow-Origin' 'https://app.midominio.es';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
¿Dónde guardar tokens en el cliente?
No usar localStorage para refresh tokens; usar HttpOnly cookies y mantener access tokens en memoria.
Si el frontend es una SPA, enviar access token en Authorization header y renovar con refresh cookie en segundo plano.
Previews seguros para editores
Para previews, usar signed cookies con expiración corta y permisos mínimos.
Snippet para signed cookie (concepto): guardar firma HMAC del path y expiración, verificar en servidor.
Paso 5: validación, sanitización y seguridad de entradas
Nunca confíe en la entrada del cliente; validar y sanitizar antes de procesar o almacenar.
Validación y sanitización recomendada
Validar tipos, tamaños y esquemas JSON en cada endpoint; usar sanitización antes de insertar en DB.
Herramienta práctica: validar JSON con esquemas (JSON Schema) y devolver errores 400 claros.
Evitar inyección y XSS en frontends
Escapar campos que puedan renderizarse en frontends y filtrar HTML si no es necesario.
Registrar intentos de payloads sospechosos y aumentar reglas WAF cuando se detecten patrones repetidos.
Paso 6: pruebas automáticas
Automatizar escaneos y alertas antes y después de cada despliegue para detectar regresiones.
Pruebas recomendadas y comandos
Ejecutar WPScan para vulnerabilidades conocidas y OWASP ZAP para pruebas dinámicas.
Comandos de ejemplo:
wpscan --url https://api.midominio.es --enumerate u,t
Añadir un escaneo de regresión en CI que falle el build si aparecen altas tasas de 4xx/5xx.
Métricas y alertas operativas
Medir TTFB, p95 y p99; alertar si p95 sube más del 30% tras aplicar reglas.
Registrar: RPS, CPU PHP‑FPM, ratio 4xx/5xx y número de requests a /wp-json por IP.
Logs para forense
Centralizar logs NGINX, PHP‑FPM y WAF en ELK o similar y conservar snapshots al detectar incidente.
REST vs GraphQL: comparativa práctica de seguridad
GraphQL reduce endpoints pero exige control sobre complejidad de consultas y validación por operación.
| Criterio |
REST |
GraphQL |
| Surface attack |
Multiples endpoints públicos |
Menos endpoints, mayor control por operación |
| Caching |
Fácil cache por URL |
Cache por query; cached persisted queries mejor |
| DOS por complejidad |
Menor riesgo por consulta simple |
Riesgo alto sin depth limit y complexity scoring |
| Autenticación |
JWT/OAuth2 por endpoint |
Mismos mecanismos, scopes por campo |
| Costo de implementación |
Bajo si ya existe REST |
Medio‑alto si se implementan mitigaciones |
Cuándo elegir GraphQL
Elegir GraphQL si se controla el cliente y se usan persisted queries y limits.
Cuándo quedarse en REST
Quedarse en REST si la API es simple y el equipo prefiere reglas de caché por URL y menor complejidad operacional.
La evidencia práctica: OWASP publicó su Top 10 indicando vectores comunes de APIs y ENISA ha destacado la creciente atención sobre APIs en informes recientes; para España, INCIBE ofrece guías aplicables al contexto nacional. OWASP
El párrafo de opinión más importante: Implementar autenticación fuerte y límites por IP funciona bien, pero solo si se mide el impacto en latencia y cacheabilidad; si no se vigila p95/p99, las reglas pueden degradar la experiencia. Por tanto configurar alertas automáticas y una estrategia de caché por compuerta permite mantener seguridad sin penalizar al usuario.
Errores que arruinan la seguridad de una API
El error más frecuente es exponer toda la REST API sin filtrar endpoints ni roles.
Confiar solo en plugins de seguridad y no aplicar controles en servidor deja una superficie amplia.
No auditar el almacenamiento de tokens en cliente facilita el token theft y la escalada de privilegios.
Configurar CORS con '*' o orígenes abiertos
Una CORS abierta permite que cualquier origen haga peticiones con credenciales expuestas.
No usar SameSite ni HttpOnly para refresh tokens facilita robo desde XSS.
No medir el coste en rendimiento
Aplicar reglas WAF sin medir p95/p99 puede disparar latencias y aumentar costes de CPU.
Si la regla multiplica el tiempo de respuesta, activar caché por compuerta o mover la regla al edge.
Cuándo no funciona este método y alternativas
Si necesita validación del plan antes del despliegue, puede solicitar una revisión técnica al equipo de mantenimiento para verificar configuraciones y pruebas.
Preguntas frecuentes
¿Qué diferencias de seguridad aporta usar JWT
JWT permite tokens cortos y rotación; Application Passwords son estáticos por usuario.
JWT ofrece expiración y revocación si se diseña correctamente; Application Passwords funcionan para integraciones simples pero son difíciles de rotar a escala.
Para proyectos headless que necesitan sesiones cortas y control de scopes, JWT con refresh rotatorio es la opción preferida.
¿Cómo se protege la REST API contra scraping
Aplicar rate‑limit, WAF en el borde y challenges para bots.
Bloquear por IP sospechosa, usar Cloudflare o ModSecurity y monitorizar patrones de acceso a /wp-json reduce scraping.
Además, definir una allowlist temporal para desarrolladores evita bloqueos accidentales en despliegues.
¿Es suficiente un plugin de seguridad para
No, un plugin solo no basta.
Un plugin ayuda, pero necesita controles en servidor, WAF y pruebas automáticas integradas en CI.
Sin reglas en el borde y sin monitorización, los plugins pueden fallar cuando el tráfico escala.
¿Cómo debo almacenar refresh tokens en un SPA?
En cookies HttpOnly con SameSite=strict y secure.
Mantener access tokens en memoria reduces exposición a XSS; usar cookies HttpOnly para refresh permite renovar sin exponer el token a JavaScript.
Si el SPA no tiene backend, usar OAuth2 Authorization Code con PKCE y un servidor intermedio para guardar refresh tokens.
¿Qué métricas escoger para medir impacto de
TTFB, p95 y p99 junto a RPS y uso CPU.
P95 indica experiencia real; p99 muestra picos raros que impactan usuarios.
Si p95 sube más de 30% tras una regla, aplicar mitigación: caché por compuerta o mover la regla al CDN.
¿Qué pruebas automatizadas incluir en CI para
WPScan, OWASP ZAP y tests de endpoints críticos.
Incluir un escaneo rápido de WPScan, pruebas dinámicas ZAP en modo no intrusivo y pruebas de carga básicas sobre /wp-json.
Bloquear despliegue si aparecen vulnerabilidades de gravedad alta o si las pruebas de regresión fallan.
¿Cómo gestionar previews de contenido para
Usar signed cookies con expiración corta y permisos mínimos.
Generar un token firmado que identifique la sesión de preview y caduca en minutos; verificar firma en servidor antes de servir contenido.
Asignar permisos mínimos al token y eliminar rutas públicas que devuelvan datos sensibles en previews.
Síntesis y checklist operativo final
La ruta segura combina controles en aplicación y en servidor, autenticación fuerte y pruebas automatizadas.
Checklist mínimo antes de producción:
- Deshabilitar endpoints no necesarios.
- Implementar JWT con refresh rotatorio o OAuth2 Authorization Code con PKCE.
- Configurar CORS restrictivo y cookies HttpOnly para refresh.
- Añadir rate‑limit nginx y reglas WAF en el borde.
- Ejecutar WPScan y OWASP ZAP; revisar logs y snapshots.
- Establecer métricas: p95 < 800ms, p99 < 1.5s; alertas si se sobrepasan.
Comandos útiles:
wpscan --url https://api.midominio.es --enumerate u,t
curl -I https://api.midominio.es/wp-json/wp/v2/posts
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
Responsables y umbrales:
- Administrador de sistemas: reglas nginx/WAF y certificados TLS.
- Desarrollador WordPress: endpoints, JWT/OAuth y pruebas.
- DPO: compliance RGPD y registro de brechas.
Plazo de verificación: realizar pruebas de seguridad y rendimiento en staging en 48–72 horas antes del deploy.
No aplica si tu sitio es un WordPress tradicional sin headless ni uso de la REST API pública, o si la instalación está en una red interna cerrada sin tráfico externo; tampoco es prioritario para prototipos temporales donde la seguridad se evaluará en producción.