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

Politique de sécurité du contenu (CSP) : le système immunitaire du Web

Prévention des attaques XSS et Magecart. Un guide définitif pour implémenter Strict CSP avec Nonces, le mode Report-Only et « strict-dynamic » dans Remix/Hydrogen.

AB
Alex B.
Politique de sécurité du contenu (CSP) : le système immunitaire du Web

En 2018, British Airways a subi une violation de données catastrophique. Des pirates ont volé les informations de carte de crédit de 380 000 clients. Ils n’ont pas pénétré dans la base de données. Ils n’ont pas deviné le mot de passe administrateur. Ils ont injecté 22 lignes de JavaScript dans la page de paiement via une bibliothèque tierce compromise. Ce script lisait silencieusement les entrées du formulaire et les envoyait à « baways.com » (un faux domaine).

Il s’agit d’une Supply Chain Attack (en particulier Formjacking ou Magecart). La partie effrayante ? Votre pare-feu (WAF) ne peut pas l’arrêter. La demande provient du navigateur de l’utilisateur et non de votre serveur.

La seule défense contre cela est la Politique de sécurité du contenu (CSP). CSP est un en-tête HTTP qui indique au navigateur : “Ce sont les domaines de confiance. Bloquez tout le reste.”

Chez Maison Code Paris, nous considérons CSP comme obligatoire pour tout site exécutant des transactions. Opérer sans, c’est comme laisser la porte du coffre-fort ouverte car « la serrure est difficile à utiliser ».

Pourquoi Maison Code en parle

Chez Maison Code Paris, nous agissons comme la conscience architecturale de nos clients. Nous héritons souvent de stacks “modernes” construites sans compréhension fondamentale de l’échelle.

Nous abordons ce sujet car il représente un point de pivot critique dans la maturité de l’ingénierie. Une mise en œuvre correcte différencie un MVP fragile d’une plateforme résiliente de niveau entreprise.

Pourquoi Maison Code applique un CSP strict

La sécurité n’est pas un module complémentaire ; c’est notre base de référence. Pour le commerce spécialisé de grande valeur, le risque de Magecart (Digital Skimming) est existentiel. Nous avons récemment audité un client potentiel et découvert un plugin jQuery malveillant récoltant les entrées de carte de crédit. Il était là depuis 6 mois. Un CSP strict aurait bloqué cela instantanément. Nous implémentons CSP basé sur Nonce sur chaque vitrine Headless que nous expédions, garantissant que seul le code auquel nous faisons explicitement confiance peut s’exécuter dans le navigateur de votre client.

Le mécanisme : la liste blanche

Par défaut, les navigateurs Web existants sont confus. Si le code HTML indique <script src="https://evil.com/hack.js">, le navigateur le charge. CSP modifie cette valeur par défaut en « Deny All ».

Politique de sécurité du contenu : src par défaut 'self' ; script-src 'soi' https://analytics.google.com ;

Avec cet en-tête, si un attaquant injecte <script src="https://evil.com/hack.js">, la console du navigateur crie en rouge : Refusé de charger le script car il viole la directive suivante de la politique de sécurité du contenu. Le script ne s’exécute jamais.

La stratégie : CSP nonce (Strict CSP)

La liste blanche des domaines (https://google.com) est fragile. Google héberge des millions de scripts (Drive, Maps, User Content). Si un attaquant parvient à télécharger un fichier sur Google Drive, il peut contourner votre liste blanche.

Le Gold Standard est CSP non basé. Un Nonce (numéro utilisé UNE FOIS) est un jeton cryptographique aléatoire généré par le serveur pour chaque requête.

  1. Génération de serveur : const nonce = crypto.randomBytes(16).toString('base64'); // "r4nd0m"
  2. En-tête : script-src 'nonce-r4nd0m'
  3. Injection HTML : <script nonce="r4nd0m" src="/app.js"></script>

Si un attaquant injecte <script>alert(1)</script>, il ne connaît pas le nom occasionnel. Le navigateur le bloque.

Implémentation dans Hydrogen (Remix)

Dans une application Server-Side Rendered (SSR) comme Hydrogen, nous générons le nom occasionnel dans entry.server.tsx.

// app/entry.server.tsx
importer { createContentSecurityPolicy } depuis '@shopify/hydrogen' ;

exporter la fonction asynchrone par défaut handleRequest (request, ResponseStatusCode, ResponseHeaders, remixContext) {
  // L'assistant d'Hydrogen génère le nom occasionnel et l'en-tête
  const { nonce, en-tête, NonceProvider } = createContentSecurityPolicy({
    // Directives standards
    defaultSrc : ["'self'"],
    imgSrc : ["'self'", 'cdn.shopify.com', 'data:', 'https://www.google-analytics.com'],
    styleSrc: ["'self'", "'unsafe-inline'"], // Valable pour les composants stylisés
    connectSrc : ["'self'", 'https://monorail-edge.shopifysvc.com', 'https://vitals.vercel-insights.com'],
    
    // La partie critique
    scriptSrc : [
      "'soi'",
      // 'strict-dynamic' indique aux navigateurs modernes : "Faites confiance à n'importe quel script chargé par un script de confiance"
      // Cela permet à GTM de charger Facebook Pixel sans que Facebook soit explicitement ajouté à la liste blanche.
      "'strict-dynamique'", 
      // Repli pour les anciens navigateurs
      'https://cdn.shopify.com',
      'https://www.googletagmanager.com' 
    ],
  });

réponseHeaders.set('Content-Security-Policy', en-tête);

  retour (
    // NonceProvider passe le nonce au composant <Scripts />
    <Fournisseur Nonce>
      <RemixServer context={remixContext} url={request.url} />
    </NonceProvider>
  );
}

La Directive “Strict Dynamique”

Les sites modernes utilisent Google Tag Manager (GTM). GTM charge Facebook Pixel. Facebook Pixel charge d’autres scripts de suivi. Vous ne pouvez pas prédire l’arborescence complète des dépendances. Cela a conduit les développeurs à activer « unsafe-eval » et « unsafe-inline », ce qui va à l’encontre de l’objectif de CSP.

strict-dynamic résout ce problème. Il dit : “Si un script est fiable (a un nom occasionnel valide), autorisez-le à charger d’autres scripts de manière dynamique.” Puisque nous faisons confiance à GTM (en le nonçant), nous faisons confiance à ce que GTM charge. Attention : cela donne beaucoup de confiance à GTM. Assurez-vous que votre conteneur GTM dispose de contrôles d’accès stricts.

Identifier les directives

Une politique robuste couvre bien plus que les scripts.

  1. base-uri 'none' : empêche le détournement de balises <base> (redirection de liens relatifs vers des sites malveillants).
  2. object-src 'none' : bloque Flash, les applets Java et <embed>.
  3. frame-ancestors 'none' : empêche le détournement de clics. Votre site ne peut pas être mis dans une Iframe.
  4. form-action 'self' : garantit que les formulaires ne peuvent être soumis qu’à votre propre API. Empêche les attaquants de modifier une action de formulaire sur leur serveur.
  5. connect-src : restreint fetch() / XHR. Même si un attaquant exécute JS, il ne peut pas envoyer les données à « evil.com ».

Déployer en toute sécurité : le mode rapport uniquement

Vous ne pouvez pas « activer » CSP le vendredi après-midi. Vous allez casser le site. Vous bloquerez le Chat Widget. Vous bloquerez l’application Avis. Vous démarrez en Mode rapport uniquement.

Content-Security-Policy-Report-Only : default-src 'self' ; ... rapport-uri /api/csp-report;

Le navigateur exécutera tout, mais il enverra un rapport de violation JSON à votre backend chaque fois qu’il aurait bloqué quelque chose.

Le point de terminaison du collecteur

Vous avez besoin d’un service pour ingérer ces rapports. Il est préférable d’utiliser Sentry ou Datadog ; ils visualisent les violations.

{
  "csp-rapport": {
    "document-uri": "https://maisoncode.paris/checkout",
    "référent": "",
    "directive-violée": "script-src",
    "directive-efficace": "script-src",
    "original-policy": "src par défaut 'self'; script-src 'nonce-xyz'",
    "blocked-uri": "https://malicious-analytics.com/tracker.js",
    "code-statut": 200
  }
}

Flux de travail :

  1. Déployez « Rapport uniquement ».
  2. Attendez 1 semaine.
  3. Analysez les journaux. “Ah, nous avons oublié de mettre la balise Pinterest sur liste blanche.”
  4. Mettre à jour la politique.
  5. Lorsque les journaux sont silencieux (uniquement les attaques réelles), passez à « Content-Security-Policy » (Enforce).

Code de refactorisation pour CSP

CSP vous oblige à écrire un meilleur code. Il bloque les gestionnaires d’événements en ligne.

Mauvais (bloqué) : <button onclick="trackClick()">Acheter</button>

Bon (autorisé) : <button id="buyBtn">Acheter</button> <script nonce="...">document.getElementById('buyBtn').addEventListener('click', trackClick);</script>

Il sépare le comportement du balisage.

10. Types de confiance (l’avenir de la prévention XSS)

CSP bloque la source du script. Trusted Types bloque le sink (innerHTML). Cela oblige les développeurs à nettoyer les chaînes avant de les injecter. div.innerHTML = string -> Bloqué. div.innerHTML = TrustedHTML.create(string) -> Autorisé. Cela élimine les vulnérabilités DOM XSS au niveau du moteur du navigateur. C’est strict (“Hard Mode”), mais pour une sécurité de niveau bancaire, c’est la fin du jeu.

11. Edge CSP (Cloudflare Workers)

Générer du CSP à l’origine (Node.js), c’est bien. L’injecter au Edge, c’est mieux. Nous utilisons Cloudflare Workers pour injecter des en-têtes de sécurité. Cela protège les actifs statiques (images, fichiers HTML nus) qui pourraient ne pas passer par la logique de l’application Node.js. Il permet également le « blocage d’urgence ». Si une bibliothèque est compromise, nous pouvons mettre à jour le CSP à la périphérie en 300 ms globalement.

13. CSP dans un monde micro-frontend

Et si vous chargez un widget de l’équipe B ? Vous ne pouvez pas partager le Nonce (il est généré sur le serveur). CSP basé sur le hachage. Au lieu de noncing, vous calculez le hachage SHA-256 du contenu du script. script-src 'sha256-K7g...'. Le navigateur hache le script en ligne. Si cela correspond, il fonctionne.

  • Pro : Statique. Peut être mis en cache.
  • Con : Si vous modifiez un caractère (même un espace), le hachage se casse. Nous l’utilisons pour les scripts stables de fournisseurs tiers qui changent rarement de version.

14. Le mot clé “Unsafe-Hashes”

Parfois, vous devez utiliser des gestionnaires d’événements en ligne (bibliothèques héritées). navigate('foo') à l’intérieur d’un onclick. Le CSP standard bloque cela. “unsafe-hashes’vous permet de mettre sur liste blanche des chaînes de gestionnaires d'événements spécifiques.script-src ‘unsafe-hashes’ ‘sha256-…’` (où le hachage est de “navigate(‘foo’)”). Cela vous permet de verrouiller le code existant sans réécrire l’intégralité du modèle d’événement DOM.

15. Conclusion

La politique de sécurité du contenu est la ceinture de sécurité du Web. Cela n’empêche pas le crash (injection), mais cela vous empêche de traverser le pare-brise (exfiltration de données). C’est fastidieux à configurer. Cela nécessite une maintenance constante à mesure que vous ajoutez de nouveaux outils marketing. Mais pour une marque de luxe qui s’adresse à des particuliers fortunés, c’est la base de la confiance.


Votre site est-il grand ouvert ?

Si vous n’envoyez pas d’en-tête CSP, vous êtes vulnérable à Magecart. Engagez nos Architectes.