🛡️ X-Frame-Options — Protección contra Clickjacking
🎯 ¿Qué hace?
Controla si tu sitio puede ser mostrado dentro de un <iframe> en otro sitio web. Impide que páginas externas incrusten tu contenido sin permiso.
⚠️ ¿Por qué importa?
Sin esta protección, un atacante puede cargar tu sitio en un iframe transparente y engañar a los usuarios para que hagan clics sin saberlo (clickjacking). Esto puede exponer credenciales, autorizar pagos o modificar configuraciones.
⚙️ Opciones de configuración
- DENY (recomendado): Bloquea cualquier intento de mostrar tu sitio en un iframe, sin excepciones.
- SAMEORIGIN: Solo permite iframes del mismo dominio. Útil si usas iframes legítimos propios.
<iframe> transparente, superpuesto exactamente sobre el botón "Participar". La víctima —que tiene sesión iniciada como administrador en otra pestaña— cree que hace clic en "Participar", pero en realidad el clic cae sobre el botón real de wp-admin oculto debajo, como "Eliminar usuario" o "Instalar plugin". Sin X-Frame-Options, el navegador permite que tu web se cargue dentro de ese iframe ajeno. Con X-Frame-Options: DENY, el navegador se niega directamente a mostrar tu sitio dentro de cualquier iframe externo, y la página del atacante muestra un hueco en blanco donde debería estar el señuelo.
📝 X-Content-Type-Options — Prevención de MIME Sniffing
🎯 ¿Qué hace?
Le dice al navegador que NO adivine el tipo de archivo (MIME sniffing). Obliga a respetar exactamente el tipo declarado en la cabecera Content-Type.
⚠️ ¿Por qué importa?
Sin este header, un navegador puede interpretar un archivo de texto como JavaScript y ejecutarlo. Esto permite a atacantes subir un archivo con extensión .txt pero con código malicioso que el navegador ejecutará.
⚙️ Configuración
El único valor válido es nosniff. Se recomienda activarlo siempre. No tiene efectos secundarios negativos.
imagen.jpg que en realidad contiene código JavaScript disfrazado de imagen. Si el navegador de un visitante intenta "adivinar" el tipo de contenido en vez de confiar en la cabecera que declara el servidor, puede llegar a interpretar ese archivo como un script y ejecutarlo en el contexto de tu web, en vez de mostrarlo como una imagen inofensiva. Con X-Content-Type-Options: nosniff, el navegador se limita estrictamente al tipo que el servidor declaró (imagen), y se niega a ejecutarlo como código aunque su contenido interno sugiera otra cosa.
🔒 X-XSS-Protection — Por Qué Ya No Lo Enviamos
🎯 ¿Qué era?
Un header que activaba un filtro anti-XSS incorporado en navegadores antiguos (Internet Explorer, versiones viejas de Chrome y Safari), pensado para detectar y bloquear intentos de inyección de scripts reflejados.
⚠️ Por qué lo hemos quitado
Chrome lo eliminó por completo en 2019 —no que dejara de tener efecto, sino que Google lo consideró más un riesgo que una ayuda, porque el propio filtro tenía fallos de seguridad documentados y era bastante fácil saltárselo. Firefox nunca llegó a implementarlo. Microsoft dejó de dar soporte a Internet Explorer en 2022. Mantener un header que ya no protege nada real, y que en su día pudo introducir sus propios problemas, no tenía sentido.
🔗 Referrer-Policy — Control de Fuga de Información Referer
🎯 ¿Qué hace?
Controla cuánta información de la URL de origen se envía en la cabecera Referer cuando los usuarios hacen clic en un enlace o cargan un recurso externo.
⚠️ ¿Por qué importa?
Sin control, la URL completa (incluyendo parámetros como tokens, IDs de sesión o datos personales) puede filtrarse a sitios externos. Esto viola la privacidad del usuario y puede exponer datos sensibles.
⚙️ Opciones de configuración
- no-referrer: No envía nunca el Referer. Máxima privacidad, pero puede romper analytics.
- same-origin: Solo envía Referer dentro del mismo dominio. No envía a sitios externos.
- strict-origin: Envía solo el origen (dominio), nunca la URL completa. Solo en conexiones HTTPS a HTTPS.
- strict-origin-when-cross-origin (recomendada): Envía URL completa dentro del mismo dominio, y solo el origen a sitios externos. El mejor balance entre funcionalidad y privacidad.
Referer al servidor externo, sin que el usuario ni el propietario del sitio externo lo pidan expresamente. Con Referrer-Policy: strict-origin-when-cross-origin, el navegador solo comparte el dominio de origen con sitios externos, nunca la ruta completa con el token, y los enlaces dentro de tu propio dominio siguen funcionando con normalidad.
🔐 HSTS — Forzar Conexión HTTPS
🎯 ¿Qué hace?
Obliga a los navegadores a conectar exclusivamente mediante HTTPS, incluso si el usuario escribe http:// o hace clic en un enlace HTTP. El navegador lo recuerda según el max-age configurado.
⚠️ ¿Por qué importa?
Sin HSTS, un atacante en la misma red (Wi-Fi público, por ejemplo) puede interceptar la primera conexión HTTP y redirigir al usuario a un sitio falso (ataque man-in-the-middle). HSTS elimina esta ventana de vulnerabilidad.
⚙️ Configuración: max-age
- max-age=0: Desactiva HSTS. Fuerza a los navegadores a olvidar la política.
- max-age=3600 (1 hora): Para pruebas. Bajo riesgo, los navegadores lo olvidan rápido.
- max-age=63072000 (2 años, recomendado): Para producción. Los navegadores recordarán forzar HTTPS por 2 años.
https:// delante. El navegador intenta primero la conexión insegura por HTTP. Un atacante en esa misma red wifi puede interceptar esa primera petición HTTP y devolver una copia falsificada de tu web para robar credenciales, antes de que se complete la redirección normal a HTTPS (esto se conoce como ataque de intermediario o "man-in-the-middle"). Con HSTS activo y habiendo visitado el sitio una vez antes por HTTPS, el navegador ni siquiera intenta la conexión insegura: fuerza HTTPS directamente en local, en el propio dispositivo, sin darle al atacante esa ventana de intercepción.
🎯 Content-Security-Policy (CSP) — La Policía del Contenido
🎯 ¿Qué hace?
Define exactamente qué fuentes están autorizadas para cargar scripts, estilos, imágenes, fuentes y otros recursos en tu sitio. Cualquier recurso no autorizado es bloqueado automáticamente por el navegador.
⚠️ ¿Por qué importa?
CSP es la defensa más potente contra XSS. Incluso si un atacante logra inyectar un script malicioso, CSP lo bloquea porque el script no proviene de una fuente autorizada. Es tu última línea de defensa.
<script> que carga código desde un dominio ajeno en una página de tu web (un fallo típico cuando un plugin no escapa correctamente la entrada del usuario). Sin CSP, el navegador de cualquier visitante que cargue esa página ejecutaría ese script sin preguntar, y el código podría robar cookies de sesión, redirigir a una web de phishing o capturar pulsaciones de teclado. Con una CSP que restringe script-src a tu propio dominio (y a las fuentes concretas que hayas autorizado explícitamente), el navegador detecta que ese dominio externo no está en la lista permitida y se niega a cargar y ejecutar el script, aunque el código malicioso ya esté insertado en el HTML de la página.
⚙️ Configuración
CSP usa un editor de texto libre donde escribes la política directamente. No hay botones de plantilla de un clic.
default-src 'self';. Esa única directiva se aplica como reserva a TODOS los tipos de recurso que no tengan su propia directiva explícita (scripts, estilos, imágenes, fuentes...), y como no incluye 'unsafe-inline' ni ningún dominio externo, bloqueará los estilos y scripts en línea que casi cualquier tema o plugin de WordPress escribe directamente en el HTML, además de cualquier recurso cargado desde un CDN externo (Google Fonts, jQuery, YouTube, etc.). No actives el interruptor CSP con este valor sin editarlo antes: prácticamente romperá la apariencia y funcionalidad de un WordPress real.
unsafe-inline, un dominio de CDN concreto o un permiso que otro sitio no necesita. Por eso el campo se deja en blanco (con la reserva mínima default-src 'self';) para que la construyas tú mismo según lo que tu sitio realmente carga, en vez de ofrecer un botón que invite a activarla a ciegas.
✍️ Cómo se escribe una política CSP (sintaxis básica)
Aunque parezca un bloque de texto intimidante, la CSP sigue una estructura muy simple, siempre igual:
- Cada directiva termina en punto y coma (
;). Es lo que separa una regla de la siguiente — si te olvidas uno, la directiva siguiente puede quedar «pegada» a la anterior y dejar de funcionar. - Dentro de una directiva, los valores van separados por espacios (nunca comas):
script-src 'self' https://ejemplo.com https://otro.com;autoriza tres fuentes distintas para scripts. - Las palabras clave especiales van entre comillas simples:
'self'(tu propio dominio),'none'(nada, prohibido del todo),'unsafe-inline'(permite código escrito directamente en el HTML). Los dominios normales nunca llevan comillas:https://fonts.googleapis.com, no'https://fonts.googleapis.com'. - Puedes autorizar un dominio completo con comodín:
https://*.googleapis.compermite cualquier subdominio de googleapis.com, en vez de tener que listar cada uno.
script-src si es un script) dentro del texto ya escrito en el campo, y añade el nuevo dominio justo antes del punto y coma de esa directiva, separado por un espacio del valor anterior. Ejemplo: si tenías script-src 'self' 'unsafe-inline'; y necesitas autorizar https://widget.ejemplo.com, lo dejas así: script-src 'self' 'unsafe-inline' https://widget.ejemplo.com;. No borres nada de lo que ya había, solo añade.
📚 Las directivas que vas a usar (cuántas hay y para qué sirve cada una)
La especificación CSP define más de 20 directivas en total, pero en un sitio WordPress normal solo vas a necesitar tocar estas 12 — son las que aparecen ya en el valor por defecto o las que más se piden al añadir un servicio nuevo:
| Directiva | Para qué sirve (qué controla) | Cuándo la tocas |
|---|---|---|
default-src | Valor de reserva: se aplica a cualquier tipo de recurso que no tenga su propia directiva específica más abajo. | Casi nunca — sirve de red de seguridad |
script-src | De dónde se puede cargar y ejecutar JavaScript (<script>, código en atributos onclick, etc.) | Al instalar un chatbot, un pixel de tracking, un widget de reseñas... |
style-src | De dónde se pueden cargar hojas de estilo CSS y estilos escritos directamente en el HTML | Al añadir una fuente tipográfica externa (Google Fonts, Adobe Fonts...) |
img-src | De dónde se pueden cargar imágenes | Si usas un CDN de imágenes, avatares externos (Gravatar), mapas embebidos... |
font-src | De dónde se pueden cargar archivos de tipografía (.woff, .ttf...) | Casi siempre va de la mano de style-src para el mismo servicio de fuentes |
connect-src | A qué dominios puede conectarse el JavaScript de la página (peticiones fetch, XMLHttpRequest, WebSockets) | Al integrar analíticas, un chat en vivo, cualquier servicio que envíe datos en segundo plano |
frame-src | Qué páginas externas se pueden incrustar dentro de un <iframe> en tu web | Al incrustar un vídeo de YouTube/Vimeo, un mapa de Google Maps, un formulario de otro servicio |
media-src | De dónde se pueden cargar archivos de audio y vídeo reproducidos con <audio>/<video> | Si alojas podcasts o vídeo propio fuera de tu dominio (ej. en un CDN aparte) |
object-src | Controla <object>, <embed> y <applet> (Flash y plugins antiguos del navegador) | Nunca — déjalo siempre en 'none', es tecnología obsoleta e insegura |
base-uri | Qué URLs puede usar la página como base para resolver enlaces relativos (etiqueta <base>) | Casi nunca — 'self' es correcto en el 99% de los casos |
form-action | A qué URLs se puede enviar (action=) un formulario de tu web | Si un formulario envía datos a un dominio externo (ej. una pasarela de pago con redirección) |
frame-ancestors | Quién puede incrustar TU web dentro de un <iframe> (protección anti-clickjacking) | Casi nunca — 'self' ya cubre la mayoría de casos |
upgrade-insecure-requests | Fuerza a que cualquier recurso pedido por HTTP se recargue automáticamente por HTTPS (no lleva valores, va sola) | Déjala siempre activada si tu sitio usa HTTPS (lo normal hoy en día) |
🔍 Cómo saber qué valor buscar cuando algo se rompe
Tienes tres formas de averiguar exactamente qué dominio le falta autorizar a tu CSP, de la más rápida a la más completa:
- Consola del navegador (F12 → pestaña «Console»): es el método más directo. Cada recurso bloqueado aparece como una línea roja que dice literalmente «Refused to load/execute... because it violates the following Content Security Policy directive: "script-src ..."» — la parte final te dice la directiva exacta, y justo antes aparece la URL completa del recurso bloqueado. Copia el dominio de esa URL y añádelo a esa directiva.
- Pestaña «Network» del navegador (F12 → «Network»): útil cuando quieres ver, antes incluso de activar CSP, de qué dominios depende realmente tu página. Recarga la página con esa pestaña abierta y verás cada petición con su dominio de origen — así sabes de antemano qué vas a tener que autorizar.
- Documentación del propio servicio externo: si instalas un chat en vivo, un formulario de pago o un widget de terceros conocido, busca en su web de ayuda «CSP» o «Content Security Policy» — la mayoría de servicios serios (Stripe, Intercom, HubSpot, etc.) publican exactamente qué dominios necesitas autorizar y en qué directiva, porque es una duda muy habitual entre sus propios clientes.
Refused to load the script 'https://widget.proveedorchat.com/loader.js' because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'". Localizas script-src en el campo de texto y le añades el dominio: pasa de script-src 'self' 'unsafe-inline'; a script-src 'self' 'unsafe-inline' https://widget.proveedorchat.com;. Guardas, recargas la página con F12 abierto otra vez — si el chat carga y ya no sale ningún error nuevo relacionado, está resuelto. Si el chat, una vez cargado, también necesita conectarse a otro dominio para enviar mensajes, verás un segundo error apuntando a connect-src y repites el mismo proceso con esa directiva.
📖 Dónde consultar la referencia completa
Con las 12 directivas de la tabla de arriba resuelves prácticamente cualquier caso real en WordPress. Si algún día necesitas una directiva más específica que no está aquí (hay más de 20 en total, algunas muy poco usadas fuera de aplicaciones web complejas), la referencia oficial y más completa es la documentación de MDN Web Docs (Mozilla), que mantiene la lista entera con ejemplos y compatibilidad por navegador — busca «MDN Content-Security-Policy» en cualquier buscador. Es la misma fuente que usan los propios desarrolladores de navegadores.
🔌 Permissions-Policy — Control de APIs del Navegador
🎯 ¿Qué hace? (explicación sencilla)
Imagina que tu web es una casa y cada visitante entra con su móvil. Este \"permiso\" le dice al navegador: \"esta web no necesita saber dónde estás, ni verte la cara, ni escucharte\". Así, aunque alguien malicioso logre colar un script en tu web, el navegador no le prestará la cámara, el micrófono ni la ubicación.
⚠️ ¿Por qué importa? (caso real)
Piensa en esto: tienes una tienda online. Un hacker encuentra un fallo en un plugin que instalaste hace 2 años y logra inyectar un script. Ese script podría, en silencio, encender la cámara del visitante que está viendo tus productos, o sacar su ubicación exacta, o grabar con el micrófono. Sin Permissions-Policy, el navegador no le dirá que no. Con esto activado, aunque el script malicioso lo intente, el navegador responderá: \"Lo siento, esta web no tiene permiso para usar la cámara\". Y el visitante nunca se entera del intento.
🧩 ¿Qué cosas bloquea exactamente?
Son funciones del teléfono o del ordenador que una web normal NO necesita para funcionar. Si tu web es un blog, una tienda o un sitio corporativo, no tienes por qué usar ninguna de estas:
📡 Ubicación
Ejemplo: Una web de repostería no necesita saber dónde vives para mostrarte un bizcocho. Riesgo: Un script espía podría rastrear a tus visitantes. Bloqueado ✅
🎥 Cámara
Ejemplo: Una web de recetas no necesita verte para enseñarte a cocinar. Riesgo: Espiar a visitantes grabándolos sin que lo sepan. Bloqueado ✅
🎤 Micrófono
Ejemplo: Un periódico digital no necesita escucharte para mostrarte noticias. Riesgo: Grabar conversaciones privadas. Bloqueado ✅
💳 Pagos / 🔔 Notificaciones
Ejemplo: La mayoría de webs usan su propio formulario de pago o plugins de terceros, no la API nativa del navegador. Riesgo: Que scripts no autorizados saquen dinero o envíen notificaciones falsas. Bloqueado ✅
🔄 Sensores / 🔗 USB
Ejemplo: Un sitio de noticias no necesita saber si estás caminando (acelerómetro) ni acceder a tus dispositivos USB. Riesgo: Saber si el usuario está quieto o en movimiento, o acceder a archivos USB sin permiso. Bloqueado ✅
🔐 SRI (Subresource Integrity) — Protección de Recursos CDN
🎯 ¿Qué hace? (explicación sencilla)
Imagina que pides una pizza a domicilio. El repartidor te trae una caja sellada con un precinto de seguridad. Si el precinto está roto, sabes que alguien la abrió y no te la comes. SRI hace exactamente eso con los archivos que tu web carga desde otros servidores (Google Fonts, jQuery, Bootstrap...): pone un \"precinto digital\" en cada archivo. Si el archivo llega modificado (por un hacker, por ejemplo), el navegador detecta que el precinto está roto y lo rechaza. El usuario ni se entera, pero está protegido.
⚠️ ¿Por qué importa? (caso real)
Casi todas las webs usan recursos externos: Google Fonts para las letras bonitas, jQuery para los efectos visuales, Bootstrap para que se vea bien en móvil, Google Analytics para medir visitas... Todos estos archivos están en servidores que NO controlas. Han pasado casos reales donde hackers lograron colar código malicioso en CDNs muy conocidos. Sin SRI, si un CDN es hackeado, tu web cargaría ese virus sin que te dieras cuenta. Con SRI, el navegador dice: \"Este archivo no coincide con su huella digital, lo bloqueo\".
⚙️ Cómo funciona en 3 pasos (fácil)
1️⃣ Escaneo automático
SeenSecure mira tu web y encuentra todos los archivos que cargas desde servidores externos (Google Fonts, jQuery, Bootstrap, iconos, etc.). Es como hacer un inventario de todo lo que entra en tu casa.
2️⃣ Se calcula la huella
Para cada archivo, SeenSecure genera una \"huella digital\" única (un código especial que identifica ese archivo exacto). Es como tomarle las huellas dactilares a cada recurso. Si el archivo cambia aunque sea una letra, la huella cambia por completo.
3️⃣ Protección activa
Cuando alguien visita tu web, el navegador compara la huella digital del archivo que llega con la que debería tener. Si coinciden → el archivo se ejecuta normal. Si no coinciden → el archivo es rechazado. Así de simple. El visitante ve la web correctamente y nunca sabe que hubo un intento de ataque.
📋 Resumen y Recomendaciones
No todos los headers tienen el mismo nivel de riesgo o urgencia. Esta tabla te ayuda a priorizar:
Activar Inmediatamente
Estos headers no tienen efectos secundarios negativos y protegen contra ataques comunes:
- ✅ X-Frame-Options: DENY — Clickjacking
- ✅ X-Content-Type-Options: nosniff — MIME sniffing
- ✅ Referrer-Policy: strict-origin-when-cross-origin
- ✅ Permissions-Policy — APIs del navegador
- ✅ SRI — Integridad de recursos CDN
Activar con Cuidado
Estos headers requieren verificación previa. Pueden bloquear el sitio si se configuran incorrectamente:
- ⚠️ HSTS — Empieza con max-age bajo (3600). Solo sube a 2 años cuando estés 100% seguro de que HTTPS nunca se desactivará.
- ⚠️ CSP — No lo actives con el valor de una sola línea que trae por defecto. Construye la política tú mismo primero en modo Monitoring, vigilando la consola del navegador. Si algo se bloquea, ajusta la política.