MAISON CODE .
/ Security · CSP · XSS · Headers · Compliance

Política de seguridad de contenidos (CSP): el sistema inmunológico de la Web

Prevención de ataques XSS y Magecart. Una guía definitiva para implementar Strict CSP con Nonces, modo de solo informe y "dinámico estricto" en Remix/Hydrogen.

AB
Alex B.
Política de seguridad de contenidos (CSP): el sistema inmunológico de la Web

En 2018, British Airways sufrió una filtración de datos catastrófica. Los piratas informáticos robaron los datos de las tarjetas de crédito de 380.000 clientes. No entraron en la base de datos. No adivinaron la contraseña de administrador. Inyectaron 22 líneas de JavaScript en la página de pago a través de una biblioteca de terceros comprometida. Este script leía silenciosamente las entradas de los formularios y las enviaba a baways.com (un dominio falso).

Este es un ataque a la cadena de suministro (específicamente Formjacking o Magecart). ¿La parte aterradora? Su firewall (WAF) no puede detenerlo. La solicitud proviene del navegador del usuario, no de su servidor.

La única defensa contra esto es la Política de seguridad de contenido (CSP). CSP es un encabezado HTTP que le dice al navegador: “Estos son los dominios confiables. Bloquee todo lo demás”.

En Maison Code Paris, consideramos que el CSP es obligatorio para cualquier sitio que ejecute transacciones. Operar sin él es como dejar la puerta de la bóveda abierta porque “la cerradura es difícil de usar”.

Por qué Maison Code habla de esto

En Maison Code Paris, actuamos como la conciencia arquitectónica de nuestros clientes. A menudo heredamos stacks “modernos” construidos sin una comprensión fundamental de la escala.

Discutimos este tema porque representa un punto de inflexión crítico en la madurez de la ingeniería. Implementarlo correctamente diferencia un MVP frágil de una plataforma resistente de nivel empresarial.

Por qué Maison Code aplica una CSP estricta

La seguridad no es un complemento; es nuestra base. Para el comercio minorista especializado de alto valor, el riesgo de Magecart (Digital Skimming) es existencial. Recientemente auditamos a un cliente potencial y encontramos un complemento jQuery malicioso que recopila entradas de tarjetas de crédito. Llevaba allí 6 meses. Un CSP estricto habría bloqueado esto instantáneamente. Implementamos CSP basado en Nonce en cada tienda Headless que enviamos, asegurando que solo el código en el que confiamos explícitamente pueda ejecutarse en el navegador de su cliente.

El mecanismo: listas blancas

De forma predeterminada, los navegadores web existentes son promiscuos. Si el HTML dice <script src="https://evil.com/hack.js">, el navegador lo carga. CSP cambia este valor predeterminado a “Denegar todo”.

Política de seguridad de contenido: default-src 'self'; script-src 'auto' https://analytics.google.com;

Con este encabezado, si un atacante inyecta <script src="https://evil.com/hack.js">, la consola del navegador grita en rojo: Se negó a cargar el script porque viola la siguiente directiva de la Política de seguridad de contenido. El script nunca se ejecuta.

La estrategia: CSP sin base (CSP estricto)

Incluir dominios en la lista blanca (https://google.com) es frágil. Google aloja millones de scripts (Drive, Maps, Contenido del usuario). Si un atacante puede cargar un archivo en Google Drive, puede eludir su lista blanca.

El estándar de oro es CSP basado en nonce. Un Nonce (Número utilizado UNA VEZ) es un token criptográfico aleatorio generado por el servidor para cada solicitud.

  1. Generación de servidor: const nonce = crypto.randomBytes(16).toString('base64'); // "r4nd0m"
  2. Encabezado: script-src 'nonce-r4nd0m'
  3. Inyección HTML: <script nonce="r4nd0m" src="/app.js"></script>

Si un atacante inyecta <script>alert(1)</script>, no conoce el nonce. El navegador lo bloquea.

Implementación en Hidrógeno (Remix)

En una aplicación Server-Side Rendered (SSR) como Hydrogen, generamos el nonce en entry.server.tsx.

// aplicación/entry.server.tsx
importar { createContentSecurityPolicy } desde '@shopify/hydrogen';

exportar la función asíncrona predeterminada handleRequest(solicitud, ResponseStatusCode, ResponseHeaders, remixContext) {
  // El ayudante de hidrógeno genera el nonce y el encabezado
  const { nonce, encabezado, NonceProvider } = createContentSecurityPolicy({
    // Directivas estándar
    defaultSrc: ["'yo'"],
    imgSrc: ["'self'", 'cdn.shopify.com', 'datos:', 'https://www.google-analytics.com'],
    styleSrc: ["'self'", "'unsafe-inline'"], // Válido para componentes con estilo
    connectSrc: ["'self'", 'https://monorail-edge.shopifysvc.com', 'https://vitals.vercel-insights.com'],
    
    // La parte crítica
    scriptSrc: [
      "'yo'",
      // 'strict-dynamic' indica a los navegadores modernos: "Confíe en cualquier secuencia de comandos cargada por una secuencia de comandos confiable"
      // Esto permite a GTM cargar Facebook Pixel sin que incluyamos Facebook explícitamente en la lista blanca.
      "'estrictamente dinámico'", 
      // Respaldo para navegadores más antiguos
      'https://cdn.shopify.com',
      'https://www.googletagmanager.com' 
    ],
  });

ResponseHeaders.set ('Política-de-seguridad-de-contenido', encabezado);

  regresar (
    // NonceProvider pasa el nonce al componente <Scripts />
    <NonceProveedor>
      <Contexto de RemixServer={remixContext} url={request.url} />
    </NonceProvider>
  );
}

La directiva “dinámica estricta”

Los sitios modernos utilizan Google Tag Manager (GTM). GTM carga el píxel de Facebook. Facebook Pixel carga otros scripts de seguimiento. No se puede predecir el árbol completo de dependencias. Esto llevó a que los desarrolladores habilitaran “unsafe-eval” y “unsafe-inline”, lo que contradice el propósito de CSP.

strict-dynamic resuelve esto. Dice: “Si un script es confiable (tiene un nonce válido), permítale cargar otros scripts dinámicamente”. Como confiamos en GTM (al no hacerlo), confiamos en lo que carga GTM. Advertencia: Esto pone mucha confianza en GTM. Asegúrese de que su contenedor GTM tenga controles de acceso estrictos.

Identificar directivas

Una política sólida cubre más que guiones.

  1. base-uri 'none': Previene el secuestro de etiquetas <base> (redireccionando enlaces relativos a sitios malignos).
  2. object-src 'none': Bloquea Flash, subprogramas de Java y <embed>.
  3. frame-ancestors 'none': Previene el Clickjacking. Su sitio no se puede colocar en un Iframe.
  4. form-action 'self': Garantiza que los formularios solo puedan enviarse a su propia API. Evita que los atacantes cambien una acción de formulario en su servidor.
  5. connect-src: restringe fetch()/XHR. Incluso si un atacante ejecuta JS, no puede enviar los datos a “evil.com”.

Implementación segura: el modo de solo informe

No se puede “activar” CSP el viernes por la tarde. Romperás el sitio. Bloquearás el widget de chat. Bloquearás la aplicación de reseñas. Comienza en Modo solo informar.

Solo informe de política de seguridad de contenido: default-src 'self'; ... informe-uri /api/csp-report;

El navegador ejecutará todo, pero enviará un informe de infracción JSON a su servidor cada vez que hubiera bloqueado algo.

El punto final del recopilador

Necesita un servicio para ingerir estos informes. Usar Sentry o Datadog es lo mejor; visualizan las violaciones.

{
  "informe-csp": {
    "document-uri": "https://maisoncode.paris/checkout",
    "referente": "",
    "directiva-violada": "script-src",
    "directiva-efectiva": "script-src",
    "original-policy": "default-src 'self'; script-src 'nonce-xyz'",
    "uri-bloqueado": "https://malicious-analytics.com/tracker.js",
    "código de estado": 200
  }
}

Flujo de trabajo:

  1. Implemente “Solo informe”.
  2. Espere 1 semana.
  3. Analizar registros. “Ah, nos olvidamos de incluir la etiqueta de Pinterest en la lista blanca”.
  4. Política de actualización.
  5. Cuando los registros estén silenciosos (solo ataques reales), cambie a “Política de seguridad de contenido” (Aplicar).

Código de refactorización para CSP

CSP te obliga a escribir mejor código. Bloquea Controladores de eventos en línea.

Malo (Bloqueado): <button onclick="trackClick()">Comprar</button>

Bueno (permitido): <botón id="buyBtn">Comprar</botón> <script nonce="...">document.getElementById('buyBtn').addEventListener('click', trackClick);</script>

Separa el comportamiento del marcado.

10. Tipos confiables (el futuro de la prevención XSS)

CSP bloquea la fuente del script. Tipos de confianza bloquea el sumidero (innerHTML). Obliga a los desarrolladores a desinfectar las cadenas antes de inyectarlas. div.innerHTML = cadena -> Bloqueado. div.innerHTML = TrustedHTML.create(cadena) -> Permitido. Esto elimina las vulnerabilidades de DOM XSS a nivel del motor del navegador. Es estricto (“Modo difícil”), pero para la seguridad de nivel bancario, es el final.

11. Edge CSP (trabajadores de Cloudflare)

Generar CSP en el origen (Node.js) es bueno. Inyectarlo en el Edge es mejor. Usamos Cloudflare Workers para inyectar encabezados de seguridad. Esto protege los activos estáticos (imágenes, archivos HTML simples) que podrían no pasar por la lógica de la aplicación Node.js. También permite el “Bloqueo de Emergencia”. Si una biblioteca se ve comprometida, podemos actualizar el CSP en Edge en 300 ms a nivel mundial.

13. CSP en un mundo de microfrontend

¿Qué pasa si cargas un widget del Equipo B? No puedes compartir el Nonce (se genera en el servidor). CSP basado en hash. En lugar de no hacer, calcula el hash SHA-256 del contenido del script. script-src 'sha256-K7g...'. El navegador codifica el script en línea. Si coincide, se ejecuta.

  • Pro: Estático. Se puede almacenar en caché.
  • Con: Si cambias un carácter (incluso un espacio), el hash se rompe. Usamos esto para scripts estables de proveedores externos que rara vez cambian de versión.

14. La palabra clave “Hashes inseguros”

A veces debes usar controladores de eventos en línea (bibliotecas heredadas). navigate('foo') dentro de un onclick. El CSP estándar bloquea esto. 'unsafe-hashes' le permite incluir en la lista blanca cadenas de controladores de eventos específicos. script-src 'unsafe-hashes' 'sha256-...' (donde hash es de “navigate(‘foo’)”). Esto le permite bloquear el código heredado sin reescribir todo el modelo de eventos DOM.

15. Conclusión

La política de seguridad de contenidos es el cinturón de seguridad de la web. No evita el choque (inyección), pero evita que salga volando a través del parabrisas (exfiltración de datos). Es tedioso de configurar. Requiere un mantenimiento constante a medida que agrega nuevas herramientas de marketing. Pero para una marca de lujo que trata con personas de alto patrimonio, es la base de confianza.


**[Contrate a nuestros arquitectos](/contact)**.