
Implementación de Content Security Policy en cabeceras HTTP de WordPress
Content Security Policy (CSP) es una capa crítica de defensa que reduce ataques como Cross-Site Scripting (XSS) y el robo de datos por inyección. En WordPress, donde los temas y plugins añaden recursos externos e inline scripts, diseñar e implementar CSP en las cabeceras HTTP requiere planificación técnica para evitar roturas funcionales. Esta guía explica conceptos, directivas prácticas, ejemplos para Apache/nginx y código para integrar CSP correctamente en WordPress, además de pruebas y despliegue controlado.
Qué es CSP y por qué importa en WordPress
CSP es una cabecera HTTP que instruye al navegador sobre qué orígenes pueden cargar recursos como scripts, estilos, imágenes o conexiones. Implementar CSP protege contra XSS y mitiga el impacto de terceros comprometidos. Además, una CSP bien diseñada puede mejorar la confianza del usuario y reducir riesgo, sin afectar negativamente al SEO si se prueba y despliega correctamente.
Principios básicos y directivas esenciales
Las directivas controlan tipos de recursos. Las más relevantes para WordPress son:
- default-src: fallback para otras directivas.
- script-src: controla scripts; aquí se aplican nonces y hashes.
- style-src: controla estilos; muchas veces requiere ‘unsafe-inline’ o hashes para estilos inline.
- img-src, connect-src, font-src: para imágenes, XHR/fetch y fuentes.
- frame-ancestors: evita que tu sitio sea embebido en iframes maliciosos.
- report-uri / report-to: para recibir reportes de violaciones en modo report-only.
Evita usar permanentemente ‘unsafe-inline’ en script-src; en su lugar utiliza nonces o hashes.
Implementación en el servidor
Siempre que sea posible, añade CSP a nivel de servidor. Esto asegura que la política se aplica antes de cualquier modificación por plugins y es más eficiente.
Apache (.htaccess o configuración vhost)
Ejemplo mínimo para producción (ajustar orígenes reales):
Header set Content-Security-Policy 'default-src 'self'; script-src 'self' https://www.google-analytics.com; style-src 'self' https://fonts.googleapis.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; upgrade-insecure-requests'
NGINX
Ejemplo de bloque server:
add_header Content-Security-Policy 'default-src 'self'; script-src 'self' https://www.google-analytics.com; style-src 'self' https://fonts.googleapis.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none'; upgrade-insecure-requests';
En nginx considera la directiva always si necesitas que la cabecera se envíe incluso en respuestas 4xx/5xx.
Integración en WordPress
Si no tienes control del servidor o necesitas lógica dinámica (nonces, políticas distintas por entorno), añade la cabecera desde WordPress usando el hook send_headers.
Generar y usar nonces en WordPress
Flujo recomendado:
- Generar un nonce por petición en init o send_headers.
- Añadir el nonce a la cabecera script-src como ‘nonce-BASE64’.
- Inyectar el atributo nonce en los scripts inline que controlas.
Ejemplo básico de generación y envío (simplificado):
add_action('send_headers', function() {
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-$nonce' https://www.google-analytics.com; style-src 'self' 'nonce-$nonce' https://fonts.googleapis.com; img-src 'self' data: https:; frame-ancestors 'none';");
// Guardar nonce para usarlo al imprimir scripts
setcookie('csp-nonce', $nonce, 0, '/', '', is_ssl(), true);
});
// Al imprimir scripts inline en plantillas
$nonce = $_COOKIE['csp-nonce'] ?? '';
echo "";
Nota: usar cookies como ejemplo puede no ser ideal para todos los escenarios; es preferible almacenar el nonce en la request global o en la instancia de WP para que esté disponible cuando se impriman scripts y estilos.
Evitar conflictos con plugins
Muchos plugins inyectan inline scripts o cargan recursos desde CDNs. Para evitar roturas:
- Identifica scripts de terceros con las herramientas de desarrollo del navegador.
- Si no puedes evitar inline scripts, usa hashes SHA256 o nonces para permitirlos.
- Considera sustituir plugins que dependen de inline JS por alternativas compatibles con CSP.
Usar hashes vs nonces
Ambas opciones permiten aprobar contenido inline sin habilitar ‘unsafe-inline’.
- Hashes: calculas el SHA256 del contenido inline y lo agregas a script-src. Es estático y seguro cuando el contenido no cambia.
- Nonces: son valores aleatorios por petición. Ideales cuando los scripts inline cambian entre peticiones o son generados dinámicamente.
Ejemplo de hash: ‘sha256-BASE64HASH’
Modo report-only y despliegue gradual
No despliegues una CSP restrictiva en producción sin pruebas. Usa report-only para recopilar violaciones sin bloquear recursos:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'nonce-abc'; report-uri https://mi-endpoint-de-reportes.example/csp-report
Recolecta reportes durante días, ajusta la política y luego cambia a enforcing. Para definir endpoints y formato de reportes consulta la documentación de MDN Web Docs y la guía de OWASP.
Reportes y monitoreo
Implementa un endpoint para recibir JSON con violaciones o usa servicios de terceros compatibles. También puedes integrar reportes con sistemas de logging internos. Revisa patrones frecuentes de bloqueo: recursos desde CDNs, analytics, fuentes web o endpoints API.
Pruebas y debugging
Pasos prácticos:
- Habilita la política en modo report-only y navega por el sitio completo.
- Revisa la consola del navegador y los reportes recibidos.
- Ajusta directivas (añade dominios confiables o hashes/nonces).
- Prueba en diferentes navegadores; el soporte de features y reportes puede variar.
Cloudflare ofrece guías prácticas para CSP y su integración con proxies; revisa su documentación cuando uses CDN o WAF en la ruta: Cloudflare Docs.
Errores comunes y soluciones
- Problema: plugins que inyectan inline scripts. Solución: usar nonces/hashes o reemplazar plugin.
- Problema: fuentes web bloqueadas. Solución: añadir orígenes en font-src y style-src.
- Problema: analytics o pixel tracking bloqueado. Solución: permitir los dominios específicos en script-src o servir una versión sin inline.
- Problema: políticas demasiado laxas con ‘unsafe-inline’. Solución: migrar gradualmente a nonces/hashes y reducir uso de inline.
Checklist rápida antes de producir
- Recopilar lista de orígenes de scripts, estilos, imágenes, fuentes y conexiones.
- Decidir entre hashes y nonces para contenido inline.
- Probar en staging con report-only durante al menos una semana.
- Revisar reportes, ajustar y desplegar política estricta en producción.
- Monitorear después del despliegue para detectar regressions.
Recursos y lecturas recomendadas
Para profundizar en especificaciones y buenas prácticas revisa las guías oficiales en MDN Web Docs y las recomendaciones de OWASP. Si usas CDN o WAF, consulta la documentación concreta del proveedor en la que se expliquen cabeceras y caching.
Conclusión
Implementar Content Security Policy en cabeceras HTTP de WordPress eleva significativamente la seguridad y reduce el riesgo de XSS y fugas de datos. La clave está en aplicar un enfoque gradual: recopilar orígenes, usar report-only para pruebas, elegir entre nonces y hashes según el caso, y desplegar en servidor o vía WordPress según tus permisos. Una CSP bien diseñada protege usuarios sin romper funcionalidades y complementa buenas prácticas de hardening en servidores, actualizaciones y control de plugins.