Retour au blog
Sécurité8 min

OWASP A10:2021 – SSRF (Server-Side Request Forgery)

Comprendre et prévenir les attaques SSRF qui ciblent les ressources internes.

Équipe Eurinhash

Analyste en Cybersécurité

Qu'est-ce que le SSRF ?

Le Server-Side Request Forgery (SSRF) est une vulnérabilité qui permet à un attaquant de faire envoyer des requêtes par le serveur vers des ressources internes ou externes non autorisées.

Cette catégorie a fait son entrée dans l'OWASP Top 10 2021 en raison de son augmentation significative, notamment avec l'essor du cloud computing.

Exemples Courants de Failles

  1. Accès aux métadonnées cloud : Récupération des credentials IAM sur AWS, Azure, GCP.
  2. Scan du réseau interne : Découverte des services exposés uniquement en interne.
  3. Contournement de firewall : Le serveur agit comme proxy vers des zones protégées.
  4. Interactions avec des services internes : Bases de données, caches, APIs administratives.

Comment se protéger ?

Sécurisez les requêtes sortantes de votre application :

  • Liste blanche de domaines : N'autorisez que les domaines explicitement approuvés.
  • Validation stricte des URLs : Rejetez les IP privées et les localhost.
  • Désactivation des redirections : Ne suivez pas automatiquement les redirections HTTP.
  • Segmentation réseau : Isolez les services sensibles du réseau interne.

[!WARNING] Le SSRF peut permettre à un attaquant d'accéder aux métadonnées de votre infrastructure cloud, compromettant ainsi tout votre environnement.

// MAUVAISE PRATIQUE : URL utilisateur non validée
const axios = require('axios');
 
app.get('/fetch', async (req, res) => {
  const url = req.query.url; // L'attaquant peut entrer http://169.254.169.254/
  const response = await axios.get(url);
  res.send(response.data);
});
 
// BONNE PRATIQUE : Validation stricte avec liste blanche
const { URL } = require('url');
const axios = require('axios');
 
const ALLOWED_HOSTS = ['api.example.com', 'cdn.example.com'];
const PRIVATE_IP_REGEX = /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.|169\.254\.)/;
 
app.get('/fetch', async (req, res) => {
  try {
    const url = new URL(req.query.url);
    
    // Vérifier le host
    if (!ALLOWED_HOSTS.includes(url.hostname)) {
      return res.status(403).json({ error: 'Domaine non autorisé' });
    }
    
    // Vérifier que ce n'est pas une IP privée
    if (PRIVATE_IP_REGEX.test(url.hostname)) {
      return res.status(403).json({ error: 'Accès interne interdit' });
    }
    
    // Désactiver les redirections
    const response = await axios.get(url.toString(), {
      maxRedirects: 0,
      validateStatus: status => status < 400
    });
    
    res.send(response.data);
  } catch (e) {
    res.status(400).json({ error: 'URL invalide' });
  }
});

Métadonnées Cloud à Protéger

FournisseurEndpointRisque
AWShttp://169.254.169.254/latest/meta-data/Credentials IAM
Azurehttp://169.254.169.254/metadata/instance/Managed Identity
GCPhttp://metadata.google.internal/computeMetadata/Service Account

[!TIP] Sur AWS, utilisez IMDSv2 qui requiert une authentification par token pour accéder aux métadonnées.

Restez en veille

Inscrivez-vous pour recevoir nos prochaines analyses techniques directement dans votre terminal.