Cuando empecé a desarrollar mi sitio web, mi principal preocupación era bastante sencilla: que funcionara.
Que el HTML estuviera bien estructurado, que el CSS se viera correctamente, que JavaScript hiciera lo que tenía que hacer y que la página se adaptara a celulares y computadoras.
Pero después apareció una pregunta que, como desarrollador web junior, considero que todos deberíamos hacernos:
¿Qué pasa con mi página una vez que la publico en Internet?
Ahí fue cuando decidí hacer una primera revisión de ciberseguridad web básica sobre mi propio proyecto.
No soy especialista en ciberseguridad y esta experiencia no pretende ser una auditoría profesional de seguridad. Es, simplemente, el proceso que seguí para aprender qué podía mejorar en una página web estática y qué herramientas podía utilizar para comprobarlo.
Para esta etapa también utilicé inteligencia artificial como herramienta de acompañamiento y aprendizaje. La idea no fue simplemente copiar configuraciones, sino entender qué estaba haciendo, por qué lo estaba haciendo y cómo comprobar que realmente funcionaba.
¿Qué es la ciberseguridad web?
La ciberseguridad web comprende un conjunto muy amplio de prácticas destinadas a proteger sitios y aplicaciones web, sus usuarios, datos e infraestructura.
Para alguien que recién comienza en desarrollo web, puede parecer un tema enorme. Y realmente lo es.
Pero también existen algunas medidas relativamente sencillas que permiten establecer una primera capa de protección.
Una de ellas son los llamados:
Los headers o encabezados HTTP son información que viaja entre el servidor y el navegador cuando se realiza una solicitud.
Por ejemplo, cuando alguien entra a:
https://terereestudio.com.ar
el navegador solicita los recursos de la página y el servidor responde con información sobre ese contenido.
Entre esa información pueden encontrarse determinados headers que le indican al navegador cómo debe comportarse frente a esos recursos.
Mi primer _headers
Como mi página estaba alojada en Netlify, encontré una solución muy práctica: utilizar un archivo llamado:
_headers
Este archivo se coloca dentro del directorio que Netlify publica.
La documentación oficial de Netlify indica que
_headers debe ser un archivo de texto plano y permite
definir headers para determinadas rutas o para todo el sitio.
Para aplicar una regla a todas las páginas se puede utilizar:
/*
En mi caso, el archivo terminó teniendo una estructura similar a esta:
/*
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Cada línea tiene un propósito diferente.
1. Strict-Transport-Security
El primer concepto que incorporé fue HSTS, o HTTP Strict Transport Security.
La configuración que utilicé fue:
Strict-Transport-Security:
max-age=31536000; includeSubDomains
En términos sencillos, le indica al navegador que, durante el período indicado, debe utilizar HTTPS para comunicarse con el sitio.
Esto ayuda a reducir determinados escenarios de ataques de intermediario (man-in-the-middle) relacionados con conexiones HTTP inseguras.
En mi caso tenía sentido porque mi sitio ya funcionaba mediante HTTPS.
2. X-Content-Type-Options
Después incorporé:
X-Content-Type-Options: nosniff
Este header le indica al navegador que respete los tipos MIME declarados y que no intente “adivinar” el tipo de contenido.
Esto ayuda a evitar determinados problemas de MIME sniffing, en los que un recurso podría ser interpretado de una manera diferente a la esperada.
Es una línea pequeña, pero fue una de esas cosas que me hicieron entender que la seguridad web también consiste en controlar cómo interpreta el navegador aquello que recibe.
3. X-Frame-Options
Otro de los headers que incorporé fue:
X-Frame-Options: DENY
Su función es impedir que la página sea mostrada dentro de determinados
elementos de otros sitios, como un <iframe>.
Una de las amenazas relacionadas con este comportamiento es el clickjacking, donde una página puede intentar engañar al usuario superponiendo o integrando contenido de manera que no sea evidente.
En mi caso, no necesitaba que mi sitio pudiera ser embebido dentro de otro sitio, así que esta configuración tenía sentido.
4. Referrer-Policy
También incorporé:
Referrer-Policy:
strict-origin-when-cross-origin
Este header controla qué información de referencia envía el navegador cuando el usuario navega desde mi sitio hacia otro recurso.
La política que utilicé permite mantener información de referencia dentro del mismo origen, mientras que en solicitudes entre diferentes orígenes limita la información enviada principalmente al origen.
Antes de estudiar este tema, nunca me había detenido a pensar que el navegador también puede estar compartiendo información contextual sobre la navegación.
5. Permissions-Policy
Finalmente agregué:
Permissions-Policy:
camera=(), microphone=(), geolocation=(), payment=()
La idea es restringir determinadas capacidades del navegador que mi página no necesita.
En mi caso no necesito acceder a:
- cámara;
- micrófono;
- ubicación;
- APIs relacionadas con pagos.
Por lo tanto, tiene sentido indicarle al navegador que esas capacidades no están permitidas para mi sitio.
No significa que esto convierta automáticamente una página en “segura”, pero sí establece límites adicionales sobre las capacidades que puede utilizar el contenido web.
¿Cómo comprobé que realmente funcionaba?
Esta fue probablemente una de las partes que más me interesó como desarrollador.
No quería simplemente agregar el archivo y asumir que estaba funcionando.
Después de hacer el deploy en Netlify, abrí mi página en Google Chrome y utilicé:
Ahí pude observar los headers que realmente estaba enviando el servidor.
Strict-Transport-Security:
max-age=31536000; includeSubDomains
X-Content-Type-Options:
nosniff
X-Frame-Options:
DENY
Referrer-Policy:
strict-origin-when-cross-origin
Permissions-Policy:
camera=(), microphone=(), geolocation=(), payment=()
Fue un aprendizaje importante porque entendí la diferencia entre:
“Configuré algo en mi proyecto.”
y:
“Comprobé que esa configuración llegó realmente al navegador.”
Las DevTools del navegador se convirtieron así en una herramienta fundamental para verificar mi trabajo.
¿Y qué pasó con Content Security Policy?
Durante esta primera etapa también llegué a otro concepto muy importante:
La CSP permite establecer qué orígenes y tipos de recursos puede cargar una página y constituye una capa adicional de defensa frente a determinados ataques, entre ellos algunos escenarios de XSS e inyección de contenido.
Sin embargo, decidí no aplicar inmediatamente una CSP restrictiva.
Primero la configuré en modo:
Content-Security-Policy-Report-Only
Este modo permite observar posibles violaciones de la política sin bloquear inmediatamente los recursos.
Me pareció una buena oportunidad para aprender algo más importante que simplemente agregar una línea de configuración:
En seguridad, antes de bloquear cosas en producción, conviene entender qué estás bloqueando.
La experiencia completa con CSP merece una entrada propia, así que será tema de un próximo artículo.
¿Esto significa que mi web es imposible de hackear?
No.
Y esta fue otra de las cosas importantes que aprendí.
Agregar Security Headers no convierte una página web en inexpugnable.
La seguridad depende de muchas capas:
- código;
- dependencias;
- infraestructura;
- formularios;
- cuentas;
- contraseñas;
- autenticación;
- servidor;
- proveedores externos;
- configuración;
- actualizaciones;
- comportamiento del usuario.
En mi caso, además de trabajar sobre la web, protegí las cuentas relacionadas con el proyecto mediante autenticación de dos factores (2FA).
Por eso prefiero pensar en seguridad como un proceso de reducción de riesgos, no como una garantía absoluta.
Lo que me llevo de esta experiencia
Probablemente el aprendizaje más importante no fue memorizar cinco headers.
Fue cambiar ligeramente mi forma de pensar como desarrollador. Después de esta experiencia empecé a pensar así:
“Mi página funciona, está publicada y ahora quiero entender qué sucede cuando un navegador la recibe.”
Aprendí a revisar los headers HTTP, utilizar Chrome DevTools,
configurar _headers en Netlify y comprobar directamente
en producción el resultado.
También aprendí que no hace falta comenzar siendo especialista en ciberseguridad para incorporar buenas prácticas de seguridad desde las primeras etapas de un proyecto.
Esta fue solamente mi primera aproximación.
Y justamente por eso decidí documentarla.
En tereré estudio creo que desarrollar una página web no consiste solamente en hacer que se vea bien.
También implica pensar en:
rendimiento · accesibilidad · seguridad · responsive · SEO · mantenimiento
Y seguir aprendiendo después de publicar.
Esta auditoría fue una pequeña parte de ese proceso.