secaudit.eu
Protección

Cabeceras de seguridad en WordPress: CSP, HSTS y más

Cabeceras de seguridad en WordPress: valores iniciales seguros para HSTS, CSP, Referrer-Policy y Permissions-Policy, dónde configurarlas y cómo evitar fallos.

Por , AngelForce OÜ 9 min de lectura

También en: English Deutsch Français Italiano Polski Português Türkçe

La mayoría de los sitios WordPress pueden corregir una mala nota en cabeceras en menos de una hora. Añada hoy cuatro cabeceras de bajo riesgo: HSTS, X-Content-Type-Options, Referrer-Policy y Permissions-Policy. Después, despliegue Content-Security-Policy poco a poco, empezando en modo solo informe. Esta guía recoge valores iniciales seguros para cada una de estas cabeceras de seguridad de WordPress, dónde configurarlas y cuáles pueden romper el editor o los contenidos incrustados.

Qué hacen las cabeceras de seguridad de WordPress y qué no

Las cabeceras de seguridad son instrucciones que su servidor envía al navegador del visitante. Le indican que use solo HTTPS, que no adivine el tipo de los archivos, que no permita que otros sitios incrusten sus páginas en un marco y qué scripts puede ejecutar. Con ello se reducen los ataques de degradación de protocolo, el MIME sniffing, el clickjacking y el impacto del cross-site scripting (XSS).

No corrigen un plugin vulnerable, no frenan un ataque de adivinación de contraseñas ni protegen el servidor en sí. Si un atacante puede escribir archivos PHP en su servidor, ninguna cabecera servirá de nada. Considérelas una capa de refuerzo del lado del navegador, no una corrección de seguridad.

La ausencia de estas cabeceras es un ejemplo clásico de configuración de seguridad incorrecta en el OWASP Top 10. Son además medidas baratas y bien documentadas, por lo que forman parte del «estado de la técnica» que el artículo 32 del RGPD pide tener en cuenta.

Diagrama: qué capas envían cabeceras HTTP en qué respuestas
Las reglas del servidor y de la CDN llegan a todas las respuestas. Las cabeceras definidas desde PHP pueden quedarse fuera de las páginas en caché y de los archivos estáticos.

Punto de partida: valores iniciales seguros para cada cabecera

Estos valores son conservadores a propósito. No deberían romper un sitio WordPress típico y podrá endurecerlos más adelante.

CabeceraQué haceValor inicial seguro
Strict-Transport-SecurityFuerza HTTPS en visitas posterioresmax-age=300
X-Content-Type-OptionsImpide adivinar el tipo MIMEnosniff
Referrer-PolicyLimita los datos de la URL enviados a otros sitiosstrict-origin-when-cross-origin
Permissions-PolicyDesactiva funciones del navegador que no usacamera=(), microphone=(), geolocation=()
Content-Security-PolicyProtección contra marcos (por ahora)frame-ancestors 'self'
X-Frame-OptionsProtección heredada contra marcosSAMEORIGIN

HSTS está definido en la RFC 6797. Empiece con cinco minutos (max-age=300) y compruebe que no se rompe nada. Después súbalo a un día (86400), luego a una semana y, por último, a un año (31536000). Añada includeSubDomains solo cuando haya verificado que todos los subdominios sirven HTTPS.

Referrer-Policy con strict-origin-when-cross-origin envía a otros sitios únicamente su dominio, no la URL completa. En WordPress esto importa porque las URL suelen llevar parámetros como términos de búsqueda, números de pedido o parámetros de campaña, y no conviene que lleguen a terceros.

Permissions-Policy desactiva la cámara, el micrófono y la geolocalización para sus páginas y para cualquier marco incrustado. Si tiene un localizador de tiendas que necesita acceder a la ubicación, deje geolocation fuera de la lista.

La protección contra marcos pertenece ahora a CSP, en forma de frame-ancestors 'self'. Mantenga X-Frame-Options: SAMEORIGIN solo como respaldo para navegadores antiguos. Una CSP pequeña, aplicada de forma efectiva y que contenga solo frame-ancestors, se puede desplegar ya sin riesgo. La CSP completa llegará después.

X-XSS-Protection está obsoleta. No la active. Controlaba un filtro XSS que los navegadores modernos han eliminado, y el propio filtro podía aprovecharse para provocar problemas nuevos en páginas que, por lo demás, eran seguras. Configurar 1; mode=block no aporta nada. La sustituta es CSP.

Dónde configurar las cabeceras: servidor, plugin o CDN

Elija un solo lugar para cada cabecera. Si el servidor, un plugin de seguridad y la CDN envían todos Strict-Transport-Security, obtendrá valores duplicados o contradictorios y nadie recordará cuál está activo.

Lo habitual es que el mejor sitio sea el servidor web. Sus reglas se aplican a todas las respuestas, incluidas imágenes, hojas CSS y páginas servidas desde una caché de página completa. En Apache, que es lo que usa la mayoría de los hostings compartidos, añada lo siguiente al archivo .htaccess de la raíz de WordPress, por encima del bloque # BEGIN WordPress. Requiere mod_headers:

<IfModule mod_headers.c>
  Header always set Strict-Transport-Security "max-age=300"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
  Header always set Content-Security-Policy "frame-ancestors 'self'"
  Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>

En nginx, coloque las líneas equivalentes en el bloque server de su sitio HTTPS y ejecute nginx -t antes de recargar:

add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;

Cuidado con una trampa de nginx. Un bloque location que tenga su propia línea add_header no hereda ninguna de las líneas add_header del nivel server. Una cabecera de control de caché añadida para /wp-content/uploads/ puede eliminar sin avisar todas sus cabeceras de seguridad en esos archivos. Repita las cabeceras en ese bloque o muévalas a un fragmento que incluya con include en todas partes.

Los plugins y el código que usan el hook send_headers de WordPress solo se ejecutan cuando PHP construye la página. Los archivos estáticos nunca pasan por PHP. Las páginas servidas desde una caché de página completa también pueden saltarse PHP, según cómo la caché almacene y reproduzca las cabeceras. Las reglas de la CDN o del borde son una buena alternativa, porque cubren todo lo que pasa por la CDN. Los hostings gestionados suelen ofrecer sus propios ajustes de cabeceras en el panel de control. Si el suyo lo hace, use ese panel y no duplique las reglas en .htaccess.

Content-Security-Policy sin romper WordPress

CSP es la cabecera con mayor beneficio y también con mayor riesgo. Indica al navegador qué orígenes de scripts, estilos, imágenes, fuentes y marcos están permitidos. WordPress lo complica: el núcleo y el editor de bloques generan scripts y estilos en línea, los constructores de páginas inyectan más, y las herramientas de analítica, los vídeos de YouTube, los mapas incrustados, los banners de cookies y los widgets de pago cargan código desde otros dominios. Una política estricta que bloquee cualquiera de ellos puede romper el proceso de compra o dejar el editor en blanco.

Empiece en modo solo informe. El navegador registra entonces las infracciones sin bloquear nada:

Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'

Abra sus principales tipos de página: la portada, una entrada del blog, una página con contenido incrustado, el carrito y la finalización de compra. Después abra las herramientas de desarrollo del navegador y revise la consola en cada una. Cada infracción indica el recurso bloqueado y la directiva incumplida. Trate esa lista como un inventario de las peticiones a terceros que hace su sitio. A menudo aparecen cosas que nadie recuerda haber añadido, como un antiguo widget de chat o fuentes cargadas desde Google. Nuestro artículo sobre Google Fonts explica cómo resolverlo.

Añada los orígenes que realmente necesite, quite los que no y vuelva a revisar la consola. Cambie el nombre de la cabecera a Content-Security-Policy solo cuando las infracciones hayan cesado en todas las páginas importantes. Incluya frame-ancestors 'self' en esa política completa: los navegadores ignoran frame-ancestors en modo solo informe, y la cabecera completa sustituirá a la pequeña que configuró antes. Los scripts en línea son la parte difícil: permitir 'unsafe-inline' elimina gran parte de la protección contra XSS, mientras que los nonces o los hashes requieren soporte del tema o del plugin. La referencia de CSP en MDN documenta todas las directivas.

Plantéese dejar wp-admin fuera de la política estricta, al menos al principio. En nginx, defina la CSP del front-end en las ubicaciones que sirven el sitio público, en lugar de en todo el bloque server.

Puede añadir un endpoint report-uri o report-to para recopilar las infracciones de forma centralizada. Esos informes contienen URL de páginas y datos del navegador, y cada uno llega con la dirección IP del visitante. Un servicio de informes de terceros suele actuar como encargado del tratamiento según el RGPD, de modo que necesita un contrato de encargado del tratamiento (art. 28). En un sitio pequeño, suele bastar con leer la consola usted mismo.

HSTS preload: por qué esperar

La precarga incluye su dominio en una lista que los navegadores traen de serie. Esos navegadores usarán entonces HTTPS para su dominio antes de la primera visita, y también para cada subdominio. Ahí está el problema. Un antiguo servidor de correo web, un sitio de pruebas, la interfaz de una impresora o una herramienta de intranet que solo funciona por HTTP plano dejan de ser accesibles en esos navegadores.

Retirar un dominio de la lista es posible, pero lento. Los navegadores solo eliminan la entrada cuando el cambio llega a los usuarios mediante actualizaciones. Actúe como si la decisión fuera permanente.

Suba max-age paso a paso y manténgalo un tiempo en los valores altos. Haga una lista de todos los subdominios que usa su organización, incluidos los que figuran en el DNS y que ya nadie visita. Considere la precarga solo cuando todos sirvan HTTPS. El sitio de envío, hstspreload.org, publica los requisitos vigentes para la cabecera. Consúltelos allí en lugar de copiar valores de artículos de blog.

Comprobar las cabeceras y mantenerlas

Compruebe las respuestas reales, no los archivos de configuración. En las herramientas de desarrollo del navegador, abra la pestaña Red, haga clic en la petición del documento y lea las cabeceras de respuesta. Desde un terminal:

curl -sI https://example.com/
curl -sI https://example.com/wp-includes/css/dist/block-library/style.min.css

Los sitios públicos de comprobación de cabeceras dan una visión rápida. El OWASP Secure Headers Project explica el razonamiento detrás de cada cabecera recomendada. Pruebe al menos estas cinco respuestas:

  • La página de inicio
  • Una página servida desde caché, abierta sin haber iniciado sesión
  • Un archivo estático (CSS, JS o imagen)
  • /wp-login.php
  • Una pantalla de wp-admin, incluido el editor de bloques

Las cabeceras tienden a desaparecer sin que nadie lo note. Las causas habituales son una migración de hosting que pierde sus reglas de .htaccess, un cambio en la CDN o un nuevo plugin de caché o de seguridad que envía sus propios valores. Vuelva a comprobar las cabeceras tras cualquiera de estos cambios. Guarde la configuración activa y la fecha de la prueba junto con el resto de sus registros de refuerzo, en la lista de comprobación de seguridad. Ese registro es lo que enseñará ante el cuestionario de un cliente o a un auditor.

El plugin de auditoría de secaudit.eu realiza un análisis de refuerzo y configuración y asigna sus hallazgos al art. 32 del RGPD y al art. 21 de la Directiva NIS2. Úselo junto con las comprobaciones de cabeceras descritas arriba → secaudit.eu

Preguntas frecuentes

¿Afectan las cabeceras de seguridad al SEO o a la velocidad?

El efecto es insignificante. Añaden unos cientos de bytes a cada respuesta. HSTS puede ahorrar la redirección de HTTP a HTTPS en las visitas repetidas.

¿Puede un plugin configurar todas las cabeceras de seguridad por mí?

Solo en parte. Un plugin cubre las páginas generadas por PHP, pero puede dejar fuera los archivos estáticos y las páginas en caché. Las reglas del servidor o de la CDN son más fiables.

¿Debo enviar X-XSS-Protection: 0 o dejarla fuera?

Déjela fuera, o envíe 0 si un cuestionario exige un valor explícito. 0 desactiva el filtro heredado. No use nunca 1; mode=block.

¿Exige el RGPD usar HSTS?

Ninguna norma del RGPD menciona cabeceras concretas. Lo que el RGPD sí exige es una seguridad adecuada que tenga en cuenta el estado de la técnica. Imponer HTTPS con HSTS es una medida estándar y barata que encaja con ese requisito.

Este artículo es información general y no constituye asesoramiento jurídico.

Compruebe su propio sitio

Security Audit & Compliance es el plugin de WordPress de secaudit.eu: busca malware, spam oculto, archivos del núcleo modificados y código vulnerable, y relaciona sus hallazgos con el art. 32 del RGPD, el art. 21 de NIS2 y el OWASP Top 10.

Ver qué comprueba el plugin
Melike Görel

Melike Görel escribe las guías de secaudit.eu sobre seguridad de WordPress y cumplimiento normativo europeo para AngelForce OÜ, la empresa de software de Tallin que está detrás de secaudit.eu.

Elaborado con ayuda de IA y verificado antes de su publicación. Este artículo es información general, no asesoramiento jurídico.