Aller au contenu

Prévenir les SSRF dans Strapi 5 : configuration et codage sécurisés

Découvrez comment empêcher le Server‑Side Request Forgery dans Strapi 5 en utilisant les paramètres de proxy, la liste blanche d’URL, les en‑têtes de sécurité, la limitation du débit et la surveillance des journaux d’audit.

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

woman in black top using Surface laptop
Photo de Christina @ wocintechchat.com M sur Unsplash

Strapi 5 peut être vulnérable aux Server‑Side Request Forgery (SSRF) lorsque les développeurs laissent le CMS récupérer des URL arbitraires — par exemple dans les webhooks ou les appels personnalisés strapi.fetch. La protection la plus simple consiste à bloquer toute requête qui ne pointe pas vers un hôte approuvé et à appliquer cette règle à la fois dans la configuration et dans le code. Ce guide montre comment exploiter les paramètres de proxy intégrés de Strapi, le middleware de sécurité, la limitation du débit et les fonctionnalités de journal d’audit, ainsi que des modèles de codage défensif, pour éliminer le risque SSRF.

1. Comprendre la surface d’exposition SSRF dans Strapi

Strapi utilise un helper interne strapi.fetch pour les appels HTTP sortants. Ces appels sont utilisés par :

  • La livraison de webhooks (URL définies par l’administrateur)
  • Les vérifications de licence et de télémétrie
  • Les plugins personnalisés qui interrogent des services externes

Si un attaquant peut fournir une URL qui résout en une adresse interne—localhost, 127.0.0.1 ou toute IP privée—il peut atteindre des services internes, des points de terminaison de métadonnées ou le socket Docker. L’avis de sécurité Strapi GHSA‑v8wj‑f5c7‑pvxf (pré‑4.25.2) a mis en avant ce problème, et le même chemin de code existe dans Strapi 5.

man programming using laptop
Photo de Danial Igdery sur Unsplash

2. Configurer un proxy strict pour les requêtes sortantes

La configuration serveur de Strapi (/config/server.js) inclut un objet proxy qui vous permet de fournir un agent HTTP/HTTPS personnalisé pour toutes les appels strapi.fetch.

js
// /config/server.js
module.exports = ({ env }) => ({
  // …autres paramètres du serveur
  proxy: {
    // Activer la gestion du proxy – laissez true sauf si vous désactivez intentionnellement
    koa: env.bool('IS_PROXIED', true),
    // Optionnel : définir l’en‑tête contenant l’IP client d’origine
    ipHeader: env('PROXY_IP_HEADER', 'X-Forwarded-For'),
    maxIpsCount: env.int('MAX_IPS_COUNT', 1),
    // --- Début de la protection SSRF ---
    fetch: {
      // Utiliser un agent vérifiant la liste blanche (voir le code ci‑dessous)
      request: (url, options) => {
        const { URL } = require('url');
        const allowedHosts = new Set([
          'api.trustedpartner.com',
          'hooks.mycompany.com',
        ]);
        const u = new URL(url);
        if (u.protocol !== 'https:') {
          throw new Error('Only HTTPS destinations are allowed');
        }
        if (!allowedHosts.has(u.host)) {
          throw new Error(`Host ${u.host} is not whitelisted`);
        }
        // Transmettre à l’implémentation réelle de fetch
        return fetch(url, options);
      },
    },
    // Optionnel : router tout le HTTP simple via un proxy d’entreprise
    http: env('HTTP_PROXY_URL'),
    https: env('HTTPS_PROXY_URL'),
  },
});

La propriété fetch surcharge le gestionnaire de requête par défaut. Elle valide l’URL avant l’envoi, rejetant tout schéma non‑HTTPS et tout hôte absent de la liste blanche. Ce point de contrôle unique protège toutes les utilisations intégrées et personnalisées de strapi.fetch.

2.1 Utiliser un agent keep‑alive tiers (optionnel)

Si vous avez besoin de réutiliser les connexions, encapsulez la logique de liste blanche autour d’un agent keep‑alive :

js
const { HttpsAgent } = require('agentkeepalive');
const keepAliveAgent = new HttpsAgent({ keepAlive: true });

fetch: {
  request: (url, options) => {
    // Validation de l’URL comme ci‑dessus …
    return fetch(url, { ...options, httpsAgent: keepAliveAgent });
  },
},

3. Renforcer l’interface des webhooks

Les webhooks sont définis par les administrateurs, mais un compte administrateur compromis pourrait injecter une URL malveillante. Appliquez également une validation côté UI :

js
// Exemple de validation dans un plugin webhook personnalisé
module.exports = ({ strapi }) => ({
  async createWebhook(ctx) {
    const { url } = ctx.request.body;
    const { URL } = require('url');
    const allowedHosts = ['hooks.mycompany.com'];
    const u = new URL(url);
    if (u.protocol !== 'https:' || !allowedHosts.includes(u.host)) {
      return ctx.badRequest('Invalid webhook URL');
    }
    // Continuer avec la création
    return strapi.plugin('webhooks').service('webhook').create(ctx.request.body);
  },
});

4. Ajouter des en‑têtes Content‑Security‑Policy (CSP)

Même si CSP protège principalement les navigateurs, configurer une directive stricte connect-src réduit la probabilité que du code front‑end malveillant déclenche des requêtes similaires à du SSRF depuis le client.

js
// /config/middlewares.js
module.exports = [
  'strapi::errors',
  'strapi::security',
  // autres middlewares …
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          "connect-src": ["'self'", "https:"],
          // Conserver les autres valeurs par défaut (img-src, script-src, etc.)
        },
      },
    },
  },
];

Le drapeau useDefaults: true garantit que le CSP de base de Strapi ('self') est conservé, tandis que le connect-src personnalisé n’autorise que les origines HTTPS.

5. Limiter le débit des points d’accès acceptant des URL

Si vous exposez une API qui permet aux utilisateurs de soumettre une URL (par ex. un point d’accès « preview fetch »), ajoutez une limitation du débit pour empêcher le balayage en force d’adresses internes.

js
// /config/plugins.js
module.exports = ({ env }) => ({
  'users-permissions': {
    ratelimit: {
      enabled: true,
      interval: 60000, // 1 minute
      max: 10, // 10 requêtes par minute par IP
    },
  },
});

Pour un contrôle plus granulaire, vous pouvez ajouter un middleware global Koa de limitation du débit :

js
// /config/middlewares.js (after existing middlewares)
const RateLimit = require('koa2-ratelimit').RateLimit;

module.exports = [
  // …middlewares précédents
  {
    name: 'global::rateLimit',
    config: {
      interval: { min: 5 }, // fenêtre de 5 minutes
      max: 100, // 100 requêtes par fenêtre par IP
      headers: true,
    },
  },
];

6. Surveiller avec les journaux d’audit

La fonctionnalité d’audit‑log de Strapi enregistre chaque création de webhook, génération de token et authentification admin. Activez‑la et conservez les logs au moins 30 jours pour détecter les tentatives SSRF suspectes.

js
// /config/admin.js
module.exports = ({ env }) => ({
  auditLogs: {
    enabled: true,
    retentionDays: 30,
    // Optionnel : limiter la taille de l’export pour éviter un DoS lors de l’extraction des journaux
    exportMaxRows: 500000,
  },
});

Lorsque qu’une requête bloquée survient (la liste blanche lève une exception), vous pouvez consigner un événement personnalisé dans le même hook de cycle de vie :

js
strapi.log.warn('Blocked SSRF attempt', { url, user: ctx.state.user?.id });
await strapi.services['admin::audit-log'].create({
  action: 'ssrf.blocked',
  user: ctx.state.user?.id,
  payload: { url },
});

Ces entrées apparaissent dans l’interface d’audit‑log et peuvent être exportées pour les audits de conformité.

7. Faire tourner régulièrement les secrets

Même si le SSRF est un problème au niveau réseau, garder JWT_SECRET, APP_KEYS et les clés d’API utilisées pour les appels sortants à jour réduit l’impact d’un serveur compromis.

  • Stockez les secrets dans des variables d’environnement, pas dans le code.
  • Faites‑les tourner au moins chaque trimestre.
  • Après rotation, invalidez les sessions existantes via l’interface admin ou en vidant le store de session.

8. Hygiène de la chaîne d’approvisionnement

Les exploits SSRF combinent souvent une dépendance vulnérable avec une requête sortante ouverte. Maintenez votre installation Strapi à jour et scannez les vulnérabilités connues :

  • Activez Dependabot ou les alertes de sécurité GitHub sur le dépôt.
  • Exécutez npm audit --audit-level=high dans le CI.
  • Abonnez‑vous au flux d’avis de sécurité Strapi (par ex. GHSA‑v8wj‑f5c7‑pvxf).

9. Checklist rapide

✅Action
1Définir une liste blanche statique des hôtes autorisés dans /config/server.js.
2Imposer le schéma HTTPS pour toutes les URL sortantes.
3Ajouter une validation d’URL dans chaque point d’accès qui accepte des URL fournies par l’utilisateur.
4Configurer le CSP de strapi::security avec un connect-src restrictif.
5Activer la limitation du débit sur les routes acceptant des URL.
6Activer les journaux d’audit et surveiller les événements ssrf.blocked.
7Faire tourner régulièrement les JWT et APP_KEYS.
8Tenir Strapi et toutes les dépendances npm à jour.
blue UTP cord
Photo de Jordan Harrison sur Unsplash

10. Quand faire appel à Revisionary

Si vous utilisez déjà le plugin Revisionary pour le versionnage du contenu, vous pouvez exploiter son intégration aux journaux d’audit pour conserver un enregistrement permanent de chaque tentative SSRF bloquée. L’export du journal d’audit du plugin facilite la production de preuves d’actions défensives lors d’audits GDPR ou de conformité interne.


En appliquant la configuration de proxy intégrée, les en‑têtes CSP, la limitation du débit et la surveillance des journaux d’audit, les développeurs peuvent éliminer la surface d’attaque SSRF dans Strapi 5 sans ajouter de bibliothèques externes. L’approche est déclarative, fonctionne pour tous les appels fetch natifs et personnalisés, et s’aligne sur les recommandations OWASP de prévention des SSRF.


Lectures complémentaires

  • OWASP Server‑Side Request Forgery Prevention Cheat Sheet
  • Documentation de configuration serveur Strapi
  • Guide du middleware de sécurité Strapi
  • Page fonctionnalité audit‑log Strapi
  • Article de blog Strapi sur les bonnes pratiques de limitation du débit
Mots-clésSSRFStrapi 5sécuritérenforcement APINode.jswebhooks

Sources

  1. Strapi security advisory GHSA‑v8wj‑f5c7‑pvxf
  2. Server Side Request Forgery Prevention Cheat Sheet (OWASP)
  3. Strapi server configuration documentation
  4. Strapi security middleware configuration
  5. Strapi audit‑logs feature
  6. How to set up rate limiting in Strapi – best practices and examples

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