Dans le paysage évolutif du développement d'applications modernes, les microservices sont devenus la norme pour créer des systèmes logiciels évolutifs et maintenables. Cependant, ce changement architectural introduit une surface d'attaque complexe que les défenses périmétriques traditionnelles peinent souvent à gérer. Parmi les vulnérabilités les plus insidieuses dans ce domaine figure la falsification de requêtes côté serveur (SSRF). Contrairement aux attaques côté client, le SSRF exploite la confiance que le serveur accorde à son propre réseau, permettant aux attaquants de manipuler le serveur afin qu'il effectue des requêtes non intentionnelles vers des ressources internes ou externes.
Comprendre le modèle de menace SSRF
Au cœur du SSRF se trouve le cas où une application récupère une ressource distante sans valider l'URL fournie par l'utilisateur. Dans une architecture monolithique, l'impact peut se limiter à la lecture de fichiers locaux ou à l'accès à des ports internes sur l'hôte même. Cependant, dans une architecture de microservices, les implications sont bien plus graves. Un seul point de terminaison vulnérable peut servir de point de pivot, permettant à un attaquant de cartographier le maillage de services internes, d'accéder à des services de métadonnées sensibles (tels que les métadonnées des instances AWS EC2) ou d'interagir avec des interfaces administratives qui ne sont pas exposées à l'internet public.
Les attaquants exploitent souvent le SSRF pour :
- Scanner les segments de réseau interne afin d'identifier les hôtes actifs.
- Accéder aux points de terminaison des métadonnées des fournisseurs de cloud pour voler des identifiants.
- Déclencher des injections de modèles côté serveur ou des inclusions côté serveur.
- Effectuer des scans de ports contre des services internes.
Stratégies de défense clés
Se protéger contre le SSRF nécessite une approche de défense en profondeur. S'appuyer sur un seul contrôle est rarement suffisant. Les stratégies suivantes constituent la base d'une stratégie robuste d'atténuation du SSRF.
1. Mettre en œuvre des listes blanches strictes
Le moyen le plus efficace de prévenir le SSRF est de s'assurer que l'application n'effectue des requêtes que vers des domaines attendus. Au lieu de blacklist des domaines malveillants connus — ce qui est une tâche impossible — les développeurs devraient maintenir une liste blanche stricte des noms d'hôtes et des plages d'IP autorisés. Si un service doit interagir avec une API tierce, ce domaine doit être explicitement ajouté à la liste blanche.
2. Imposer des restrictions de protocole
Les vulnérabilités surviennent souvent en raison de la prise en charge de protocoles dangereux tels que file://, gopher:// ou dict://. En restreignant l'application à l'utilisation uniquement de protocoles sûrs comme HTTP et HTTPS, vous réduisez considérablement le risque d'exploitation. De plus, assurez-vous que les requêtes ne suivent pas les redirections vers des destinations non autorisées, car les chaînes de redirection peuvent être utilisées pour contourner les vérifications initiales des URL.
3. Filtrage de la sortie et segmentation du réseau
Même si un attaquant contourne les vérifications au niveau de l'application, les contrôles au niveau du réseau peuvent l'arrêter. La configuration de règles de pare-feu pour restreindre le trafic sortant des microservices aux seuls ports et IP nécessaires est cruciale. Dans les environnements cloud, l'utilisation de groupes de sécurité ou de points de terminaison VPC garantit que les services ne peuvent pas communiquer avec le point de terminaison du service de métadonnées ou d'autres services internes, sauf autorisation explicite.
Mise en œuvre pratique : Exemple en Python
Examinons un exemple pratique de mise en œuvre d'un client HTTP sécurisé contre le SSRF en Python. Le code suivant montre comment valider les URL par rapport à une liste blanche et s'assurer que les redirections sont gérées de manière sécurisée.
import requests
from urllib.parse import urlparse
ALLOWED_HOSTS = ['api.trusted-partner.com', 'internal-service.local']
DISALLOWED_PROTOCOLS = ['file', 'gopher', 'dict']
def safe_fetch(url):
parsed_url = urlparse(url)
# Vérifier le protocole
if parsed_url.scheme in DISALLOWED_PROTOCOLS:
raise ValueError("Protocole non autorisé utilisé")
# Vérifier l'hôte par rapport à la liste blanche
if parsed_url.hostname not in ALLOWED_HOSTS:
raise ValueError("Hôte non présent dans la liste blanche")
# Récupérer avec le suivi des redirections désactivé
response = requests.get(url, allow_redirects=False, timeout=5)
return response.text
# Exemple d'utilisation
try:
# Cela déclencherait une ValueError
safe_fetch("http://169.254.169.254/latest/meta-data/")
except ValueError as e:
print(f"Bloqué : {e}")
Dans cet exemple, nous analysons l'URL pour inspecter le schéma et le nom d'hôte avant d'effectuer la requête. Nous désactivons explicitement le suivi des redirections pour empêcher les attaquants de chaîner les requêtes afin de contourner les vérifications initiales.
Conclusion
Sécuriser les microservices contre le SSRF ne consiste pas seulement à valider les entrées ; il s'agit de repenser la manière dont les services communiquent. En combinant des listes blanches strictes, des restrictions de protocole et une segmentation réseau robuste, les organisations peuvent considérablement renforcer leur infrastructure contre cette vulnérabilité puissante. En tant que développeurs, nous devons rester vigilants, reconnaissant que la confiance du serveur envers son propre réseau est à la fois une fonctionnalité et un passif potentiel. Des audits de sécurité réguliers, l'analyse statique et une surveillance proactive sont essentiels pour maintenir un écosystème de microservices sécurisé.