Cabeceras de seguridad para aplicaciones web: guía de configuración
Configura las cabeceras de seguridad esenciales para tu app de transferencia. CSP, HSTS, X-Frame-Options y más contra ataques web comunes.
Las cabeceras de seguridad son campos de respuesta HTTP que indican a los navegadores cómo restringir el comportamiento de una página. Para una app de transferencia de archivos que realiza cifrado AES-256-GCM en el cliente, seis cabeceras importan especialmente: Strict-Transport-Security para forzar HTTPS, Content-Security-Policy para bloquear la inyección de scripts, X-Frame-Options para prevenir clickjacking, Referrer-Policy para evitar filtraciones del fragmento de URL, Permissions-Policy para deshabilitar APIs del navegador no utilizadas, y Cross-Origin-Opener-Policy para aislar el contexto de navegación. Configúralas bien y bloqueas el 80% de los ataques prácticos del lado del cliente sin cambiar una sola línea de JavaScript.
HSTS y la lista de precarga
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload indica a los navegadores que nunca se conecten de nuevo en HTTP simple, durante dos años. La directiva preload te habilita para la lista de precarga HSTS de Chrome (hstspreload.org), que se distribuye con el navegador — incluso la primera petición a tu dominio va por HTTPS, eliminando la ventana inicial de MITM. La inclusión es un proceso de un solo sentido; la eliminación tarda meses. Prueba primero con max-age=300 durante unos días. Un servicio de transferencia sin precarga HSTS está a un secuestro de DNS de distancia de servir un formulario de subida falso.
Una Content Security Policy que realmente funcione
CSP es la cabecera más difícil de desplegar y la más valiosa. Una política estricta para una app de transferencia de archivos tiene este aspecto: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self' https://upload.hexatransfer.com; style-src 'self'; img-src 'self' data:; font-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'. Sin scripts inline, sin eval excepto wasm, sin conexiones a terceros. Mueve los manejadores de eventos inline a addEventListener en archivos externos. 'wasm-unsafe-eval' es necesario para libsodium.js; sin él, Argon2id no se instanciará.
Subresource Integrity en cada bundle
Si tu JavaScript carga desde una CDN como jsDelivr o unpkg, añade atributos integrity="sha384-..." en cada etiqueta de script. El navegador se niega a ejecutar scripts cuyo hash no coincida — una CDN comprometida no puede enviar silenciosamente un bundle con puerta trasera. Para scripts autoalojados servidos desde tu propio dominio, SRI es menos crítico pero igualmente defendible. Combina SRI con require-sri-for script style de CSP (obsoleto pero Chrome aún lo respeta) o aplícalo mediante el pipeline de build. HexaTransfer publica hashes por versión para que los usuarios más exigentes puedan verificar manualmente.
X-Frame-Options y frame-ancestors
Los ataques de clickjacking cargan tu página de subida en un iframe transparente sobre otra página, engañando a los usuarios para que hagan clic en "enviar" en un archivo que no pretendían compartir. X-Frame-Options: DENY bloquea todo enmarcado; el reemplazo moderno es Content-Security-Policy: frame-ancestors 'none'. Incluye ambos — los navegadores más antiguos respetan solo la cabecera heredada, los más nuevos prefieren CSP. Nunca uses SAMEORIGIN para una app de transferencia de archivos; no tienes ninguna razón legítima para enmarcar la interfaz de subida desde otra página.
Referrer-Policy para evitar filtraciones del fragmento
Los navegadores eliminan los fragmentos de URL (la parte #key=... donde vive tu clave de descifrado) de las cabeceras Referer por defecto, pero la parte de la ruta sigue filtrándose. Referrer-Policy: no-referrer bloquea toda información de referrer — sin cabecera Referer en los enlaces salientes, sin filtraciones entre orígenes, sin fingerprinting de URL por rastreadores externos. Para una página de descarga en /d/7Kj9xQmN2vP8rBwLsE4fT#k=abc, esto evita que el slug se filtre a cualquier dominio externo al que el usuario haga clic. Configúralo a nivel de sitio mediante cabecera; no confíes en rel="noreferrer" por enlace.
Permissions-Policy para defensa en profundidad
Si tu app no usa la cámara, el micrófono, la geolocalización ni la API USB, deniégalos explícitamente: Permissions-Policy: camera=(), microphone=(), geolocation=(), usb=(), bluetooth=(), accelerometer=(), magnetometer=(), gyroscope=(), payment=(). Un XSS que se cuele por CSP aún no podrá activar la webcam para grabar la cara del usuario. Esta cabecera es barata de desplegar, no tiene impacto en la UX para un flujo de transferencia de archivos y señala a los revisores de seguridad que has pensado en el sandboxing.
Cabeceras de aislamiento de origen cruzado
Para acceder a temporizadores de alta resolución y SharedArrayBuffer (necesarios para algunas implementaciones wasm de criptografía), los navegadores requieren aislamiento de origen cruzado mediante Cross-Origin-Opener-Policy: same-origin y Cross-Origin-Embedder-Policy: require-corp. Estas cabeceras también previenen ataques de canal lateral tipo Spectre que podrían filtrar claves AES desde orígenes adyacentes. La compensación: los recursos de terceros incrustados (vídeos de YouTube, checkout de Stripe) se rompen a menos que sirvan Cross-Origin-Resource-Policy: cross-origin. Para una app de transferencia de archivos de propósito único sin incrustaciones de terceros, el aislamiento es gratuito.
Cache-Control para páginas sensibles
La página de confirmación de descarga puede mostrar una clave de descifrado de corta duración o un token de sesión. Prevén el almacenamiento en caché: Cache-Control: no-store, must-revalidate y Pragma: no-cache. No confíes solo en no-cache — eso permite revalidación contra el origen, lo que significa que el almacenamiento en caché sigue produciéndose. Para el bundle JavaScript de la página de subida, aplica lo contrario: max-age largo con nombres de archivo con hash de contenido (/js/main-a7b3c9.js) para que el almacenamiento en caché de CDN funcione sin problemas de invalidación. Aplica las cabeceras de forma quirúrgica por ruta.
X-Content-Type-Options y el sniffing MIME
X-Content-Type-Options: nosniff impide que los navegadores adivinen tipos MIME basándose en el contenido. Sin ella, un archivo .txt subido por un atacante que contenga <script> podría renderizarse como HTML al servirlo de vuelta. Para un servicio de transferencia que sirve descargas controladas por el usuario, combina nosniff con Content-Disposition: attachment; filename="..." para que el navegador descargue en lugar de renderizar. Valida el nombre de archivo contra path traversal (../../../etc/passwd) en el servidor; no confíes en los metadatos de la subida para nada más que la visualización.
Monitorización e informes CSP
Despliega CSP en modo report-only primero, recopila violaciones en un endpoint report-uri durante una semana, soluciona los problemas legítimos, y luego aplica. Usa Content-Security-Policy-Report-Only para la observación, cambia a Content-Security-Policy para la aplicación. El endpoint de informes recibe POSTs JSON que describen cada violación: URI bloqueada, directiva violada, archivo fuente, número de línea. Servicios como Report URI o un Sentry autoalojado los recopilan. Revisa semanalmente — los ataques genuinos aparecen como URIs bloqueadas inusuales que nunca has visto antes.
Calificar tu trabajo
Ejecuta tu URL de producción a través de securityheaders.com y Mozilla Observatory. Una calificación A o A+ no es perfección, pero es el mínimo exigible. La mayoría de los competidores en transferencia de archivos obtienen B o peor porque olvidan Permissions-Policy o permiten 'unsafe-inline' en CSP. Un servicio pequeño puede fácilmente superar la puntuación de cabeceras de seguridad de WeTransfer; las cabeceras no cuestan nada añadirlas, y el historial de "¿por qué tu CSP no es estricta?" en conversaciones con auditores SOC 2 es más corto cuando empezaste siendo estricto.
Pruébalo en hexatransfer.com — gratis, sin cuenta, hasta 10 GB.
Envía archivos grandes de forma segura con cifrado de extremo a extremo
Transfiere archivos de hasta 10 GB gratis con cifrado de extremo a extremo. Sin necesidad de cuenta. Tus archivos se cifran en tu navegador antes de subirlos: nadie más puede leerlos.
Enviar un archivo