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
- Accès aux métadonnées cloud : Récupération des credentials IAM sur AWS, Azure, GCP.
- Scan du réseau interne : Découverte des services exposés uniquement en interne.
- Contournement de firewall : Le serveur agit comme proxy vers des zones protégées.
- 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
| Fournisseur | Endpoint | Risque |
|---|---|---|
| AWS | http://169.254.169.254/latest/meta-data/ | Credentials IAM |
| Azure | http://169.254.169.254/metadata/instance/ | Managed Identity |
| GCP | http://metadata.google.internal/computeMetadata/ | Service Account |
[!TIP] Sur AWS, utilisez IMDSv2 qui requiert une authentification par token pour accéder aux métadonnées.