Aller au contenu

Configurer les en‑têtes de sécurité et la CSP pour Strapi + Next.js – guide défensif

Apprenez pas à pas comment ajouter les en‑têtes HTTP essentiels et une CSP stricte à un CMS headless Strapi et à un frontend Next.js, pour vous protéger contre les attaques XSS, le clickjacking et les fuites de données.

Par l’équipe Revisionary · · 5 min de lecture

shallow focus photo of person using MacBook
Photo de Christina @ wocintechchat.com M sur Unsplash

Comment ajouter des en‑têtes de sécurité et une CSP stricte à un site Strapi + Next.js ?

Ajoutez les en‑têtes HTTP essentielles tant dans la couche API (Strapi) que dans la couche de présentation (Next.js). Utilisez une Content‑Security‑Policy (CSP) qui autorise uniquement les ressources dont vous avez besoin, puis verrouillez le reste avec Helmet, strict‑transport‑security, X‑Frame‑Options et les en‑têtes associés. Les étapes ci‑dessous couvrent la configuration, les tests et la maintenance d’une mise en place défensive de niveau production.

Pourquoi les en‑têtes de sécurité sont importantes

Les en‑têtes de sécurité sont des instructions envoyées par le serveur qui indiquent aux navigateurs comment gérer la réponse. Elles atténuent de nombreuses attaques courantes sans modifier le code de l’application :

En‑têteProtection principaleValeur typique
Content‑Security‑PolicyCross‑site scripting (XSS), injection de données, contenu mixtedefault-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none';
X‑Content‑Type‑OptionsMIME‑sniffingnosniff
X‑Frame‑OptionsClickjackingDENY ou SAMEORIGIN
Strict‑Transport‑Security (STS)Imposition du HTTPSmax-age=31536000; includeSubDomains; preload
Referrer‑PolicyFuite du référentstrict-origin-when-cross-origin
Permissions‑PolicyContrôle des fonctionnalités du navigateur (camera, géolocalisation…)geolocation=(), camera=()

Lorsqu’un CMS headless alimente un framework front‑end, il faut définir ces en‑têtes dans les deux environnements : l’API Strapi (qui sert du JSON) et le serveur Next.js (qui sert du HTML, JS, CSS, images). Ainsi, même si un attaquant compromettait une couche, l’autre appliquerait toujours les mêmes politiques.

A security and privacy dashboard with its status
Photo de Zulfugar Karimov sur Unsplash

Prérequis

  • Strapi 5 installé et en cours d’exécution.
  • Projet Next.js qui récupère le contenu via l’API REST ou GraphQL de Strapi.
  • Accès à la configuration du serveur (environnement Node.js) — aucun reverse‑proxy n’est requis, mais vous pouvez également appliquer les mêmes en‑têtes en bordure (Vercel, Cloudflare) si vous le souhaitez.
  • Optionnel : le plugin Revisionary installé si vous désirez un journal d’audit immuable des changements de configuration d’en‑têtes (utile pour la conformité RGPD).

1. Ajouter les en‑têtes de sécurité à Strapi

Strapi ne propose pas de gestionnaire d’en‑têtes intégré, mais vous pouvez utiliser le middleware helmet très répandu. Créez un fichier middleware personnalisé et enregistrez‑le dans config/middleware.ts.

js
// ./src/middleware/securityHeaders.js
import helmet from 'helmet';

export default helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "https://cdn.example.com"],
      styleSrc: ["'self'", "'unsafe-inline'"], // ajustez si vous utilisez des styles en ligne
      imgSrc: ["'self'", "data:", "https://images.example.com"],
      connectSrc: ["'self'", "https://api.example.com"],
      fontSrc: ["'self'", "https://fonts.gstatic.com"],
      objectSrc: ["'none'"],
      upgradeInsecureRequests: [],
    },
  },
  // En‑têtes supplémentaires
  referrerPolicy: { policy: "strict-origin-when-cross-origin" },
  hidePoweredBy: true,
  hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
  frameguard: { action: "deny" },
  noSniff: true,
});

Enregistrez maintenant le middleware :

js
// ./config/middleware.ts
module.exports = [
  'strapi::errors',
  'strapi::security',
  // autres middlewares par défaut …
  {
    name: 'global::securityHeaders',
    // Le chemin est relatif à la racine du projet
    resolve: './src/middleware/securityHeaders.js',
  },
];

Points clés

  • Les directives CSP reflètent ce dont le front‑end a besoin. Gardez la liste la plus restreinte possible.
  • upgradeInsecureRequests force les navigateurs à charger les ressources HTTP via HTTPS, évitant ainsi les avertissements de contenu mixte.
  • hidePoweredBy supprime l’en‑tête X-Powered-By: Strapi, limitant les fuites d’informations.

Vérifier les en‑têtes

Démarrez Strapi et interrogez n’importe quel endpoint, par ex. curl -I http://localhost:1337/api/articles. Vous devez voir la liste complète des en‑têtes.

2. Ajouter les en‑têtes de sécurité à Next.js

Next.js vous permet de définir des en‑têtes HTTP personnalisées dans next.config.js via la fonction asynchrone headers. Combinez cela avec le package next-secure-headers pour une configuration concise.

js
// next.config.js
const { createSecureHeaders } = require('next-secure-headers');

module.exports = {
  async headers() {
    return [
      {
        // Appliquer à toutes les routes
        source: '/(.*)',
        headers: createSecureHeaders({
          contentSecurityPolicy: {
            // Mêmes directives que Strapi, mais vous pouvez ajouter des sources spécifiques à Next
            directives: {
              defaultSrc: "'self'",
              scriptSrc: ["'self'", "https://cdn.example.com"],
              styleSrc: ["'self'", "'unsafe-inline'"],
              imgSrc: ["'self'", "data:", "https://images.example.com"],
              connectSrc: ["'self'", "https://api.example.com"],
              fontSrc: ["'self'", "https://fonts.gstatic.com"],
              objectSrc: "'none'",
              upgradeInsecureRequests: true,
            },
          },
          // Autres en‑têtes
          referrerPolicy: 'strict-origin-when-cross-origin',
          permissionsPolicy: {
            camera: [],
            microphone: [],
            geolocation: [],
          },
          // HSTS – ne fonctionne que sur les domaines de production en HTTPS
          strictTransportSecurity: {
            maxAge: 31536000,
            includeSubDomains: true,
            preload: true,
          },
          xssProtection: true,
          frameGuard: 'deny',
          noSniff: true,
        }),
      },
    ];
  },
};

Explication

  • createSecureHeaders génère l’ensemble complet des en‑têtes ; vous pouvez personnaliser chaque directive.
  • La CSP ici correspond à celle de l’API Strapi, assurant que les deux couches s’accordent sur les sources autorisées.
  • Déployez sur un hôte compatible HTTPS (Vercel, Netlify, etc.) pour que le HSTS prenne effet.

Tester dans le navigateur

Ouvrez le site sous Chrome, ouvrez les DevTools → Network, sélectionnez le document principal et inspectez l’onglet Response Headers. Vérifiez que chaque en‑tête apparaît une seule fois.

3. Aligner la CSP entre l’API et le front‑end

Comme Strapi renvoie du JSON, la CSP de l’API n’affecte pas directement le navigateur. Cependant, une politique incohérente peut provoquer des échecs de pré‑vol CORS lorsque le front‑end tente de récupérer des ressources depuis l’API.

  • Conservez la directive connect-src de la CSP du front‑end en phase avec l’origine de l’API.
  • Si vous utilisez un CDN pour les assets statiques, ajoutez‑le à la fois à script-src et à style-src.
  • Lors de l’ajout d’un script d’analyse tiers, mettez à jour les deux CSP simultanément.

4. Automatiser la validation des en‑têtes en CI/CD

Ajoutez un test simple à votre pipeline CI qui échoue si un en‑tête requis est absent. Exemple avec Node fetch et Jest :

js
// __tests__/securityHeaders.test.js
const fetch = require('node-fetch');

test('L’API renvoie les en‑têtes de sécurité obligatoires', async () => {
  const res = await fetch('http://localhost:1337/api/articles');
  const headers = res.headers;
  expect(headers.get('content-security-policy')).toBeTruthy();
  expect(headers.get('strict-transport-security')).toMatch(/max-age=31536000/);
  expect(headers.get('x-frame-options')).toBe('DENY');
  expect(headers.get('x-content-type-options')).toBe('nosniff');
});

Exécutez ce test dans vos GitHub Actions ou tout autre système CI. Si un en‑tête est supprimé ou modifié par inadvertance, le build échoue, empêchant une version vulnérable d’être déployée.

5. Checklist de maintenance continue

FréquenceTâche
À chaque nouvelle dépendanceRevoir la CSP afin d’ajouter toute source de script ou de style requise. Mettre à jour les configurations de Strapi et Next.js.
TrimestrielLancer un scanner CSP (ex. : Mozilla Observatory) contre le site en production. Corriger les violations signalées.
Après toute modification d’infrastructureVérifier que l’en‑tête strict-transport-security est toujours présent et que le flag preload correspond à l’enregistrement du domaine.
Lorsque des données liées au RGPD sont stockéesS’assurer que le journal d’audit (Revisionary) enregistre les changements d’en‑têtes. Anonymiser les IP si nécessaire (/docs/settings/privacy).
Après un incident de sécuritéFaire pivoter les tokens secrets (API keys, clés de signature JWT) et ré‑évaluer la CSP face aux nouvelles vecteurs d’attaque.

6. Pièges courants et comment les éviter

  1. CSP trop permissive – Utiliser script-src 'unsafe-inline' annule la protection XSS. Remplacez les scripts inline par des fichiers externes ou utilisez un nonce/hachage si indispensable.
  2. Oubli de upgrade-insecure-requests – Les navigateurs bloqueront le contenu mixte sur les pages HTTPS, entraînant une UI cassée. Ajoutez cette directive.
  3. En‑têtes dupliqués – Si vous définissez les en‑têtes à la fois dans un reverse‑proxy (ex. : Nginx) et dans l’application, le navigateur peut recevoir deux valeurs et ignorer la politique. Choisissez une source de vérité unique.
  4. Sous‑domaines oubliés – STS ne s’applique qu’au domaine qui envoie l’en‑tête. Ajoutez includeSubDomains et inscrivez le domaine sur la liste de pré‑chargement HSTS si vous possédez des sous‑domaines.
  5. Rapports de violation CSP ignorés – Activez report-uri ou report-to dans la CSP pour collecter les rapports de violation. Utilisez un endpoint léger (ex. : un type de collection Strapi) pour les stocker et les analyser plus tard.

7. Lier Revisionary pour la conformité d’audit (optionnel)

Si votre organisation doit prouver que les configurations d’en‑têtes n’ont pas été altérées, activez le journal d’audit de Revisionary. Chaque modification de config/middleware.ts ou de next.config.js doit être commitée via le pipeline CI qui crée également une entrée versionnée dans le CMS. Le journal (/docs/everyday-use/audit) indique qui a changé quoi et quand, facilitant la documentation RGPD.

a golden padlock sitting on top of a keyboard
Photo de Towfiqu barbhuiya sur Unsplash

Conclusion

Définir les en‑têtes de sécurité et une CSP stricte à la fois dans Strapi et dans Next.js crée une défense en profondeur contre les attaques XSS, le clickjacking, les fuites de données et le contenu mixte. En utilisant Helmet côté API et next-secure-headers côté front‑end, vous obtenez une configuration concise et maintenable. Coupez le tout avec des vérifications automatiques en CI, des scans CSP réguliers et un journal d’audit immuable (Revisionary) pour rester conforme au RGPD et aux meilleures pratiques du secteur. Mettez en œuvre les étapes ci‑dessus dès aujourd’hui et réduisez la surface d’attaque de votre application web pilotée par un CMS headless.

Mots-cléssécuritéCSPen‑têtes de sécuritéStrapiNext.js

L’équipe Revisionary

Les développeurs de Revisionary, un plugin d’historique des versions pour Strapi 5. Nous écrivons sur Strapi, le développement web, l’IA et la sécurité des applications.

Cet article a été rédigé avec l'aide d'une IA.

Tous les articles