Description
Les failles de falsification de requête côté serveur (SSRF) se produisent lorsqu'une application web permet à un attaquant de forcer le serveur à effectuer des requêtes HTTP vers des destinations arbitraires. Cela permet à un attaquant d'accéder à des ressources internes, d'interagir avec des services non exposés publiquement, et potentiellement de compromettre l'infrastructure interne.
Exemple d'Attaque SSRF
Scénario d'attaque :
- Une application web propose une fonctionnalité de prévisualisation d'URL ou de téléchargement de contenu distant, par exemple un service qui récupère des images depuis une URL fournie par l'utilisateur.
Exemple de code vulnérable :
from flask import Flask, request
import requests
app = Flask(__name__)
@app.route('/fetch', methods=['POST'])
def fetch_url():
url = request.form['url']
response = requests.get(url)
return response.contentExploitation :
- Un attaquant peut manipuler l'URL pour accéder à des ressources internes, par exemple en entrant
http://127.0.0.1:22pour scanner les ports locaux.
POST /fetch HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
url=http://127.0.0.1:6379/- Le serveur effectuerait alors une requête vers Redis sur localhost, révélant potentiellement des informations sensibles.
Prévention des Attaques SSRF
Validation et Filtrage des URLs :
- Valider les URLs fournies par l'utilisateur et bloquer l'accès aux plages d'adresses privées et localhost.
import ipaddress
from urllib.parse import urlparse
def is_safe_url(url):
try:
parsed = urlparse(url)
if parsed.scheme not in ['http', 'https']:
return False
ip = ipaddress.ip_address(parsed.hostname)
if ip.is_private or ip.is_loopback or ip.is_link_local:
return False
return True
except:
return False
@app.route('/fetch', methods=['POST'])
def fetch_url():
url = request.form['url']
if not is_safe_url(url):
return "Invalid URL!"
response = requests.get(url)
return response.contentListe blanche de domaines autorisés :
- Maintenir une liste de domaines autorisés et ne permettre l'accès qu'à ces domaines.
ALLOWED_DOMAINS = ['api.example.com', 'images.trusted-site.com']
@app.route('/fetch', methods=['POST'])
def fetch_url():
url = request.form['url']
parsed = urlparse(url)
if parsed.hostname not in ALLOWED_DOMAINS:
return "Domain not allowed!"
response = requests.get(url)
return response.contentUtilisation de proxies et isolation réseau :
- Configurer des proxies dédiés ou utiliser des conteneurs isolés pour les requêtes externes.
@app.route('/fetch', methods=['POST'])
def fetch_url():
url = request.form['url']
proxies = {
'http': 'http://proxy-server:8080',
'https': 'https://proxy-server:8080'
}
response = requests.get(url, proxies=proxies, timeout=5)
return response.contentDétection des Failles SSRF
Revue de Code :
- Examiner le code source pour identifier les endroits où l'application effectue des requêtes HTTP basées sur des entrées utilisateur.
Tests Manuels :
- Effectuer des tests manuels en manipulant les paramètres d'URL pour tenter d'accéder à des ressources internes.
Utilisation d'Outils de Sécurité :
- Utiliser des outils de sécurité tels que Burp Suite, OWASP ZAP, ou des scanners spécifiques pour détecter les failles SSRF.
Exemples de Prévention
Validation robuste avec DNS resolution :
import socket
import ipaddress
def resolve_and_validate(hostname):
try:
ip = socket.gethostbyname(hostname)
ip_obj = ipaddress.ip_address(ip)
if ip_obj.is_private or ip_obj.is_loopback:
return False
return True
except:
return False
@app.route('/fetch', methods=['POST'])
def fetch_url():
url = request.form['url']
parsed = urlparse(url)
if not resolve_and_validate(parsed.hostname):
return "Invalid destination!"
response = requests.get(url, timeout=10)
return response.contentConfiguration de timeouts et limitations :
@app.route('/fetch', methods=['POST'])
def fetch_url():
url = request.form['url']
try:
response = requests.get(
url,
timeout=5,
allow_redirects=False,
headers={'User-Agent': 'SafeBot/1.0'}
)
return response.content
except requests.exceptions.RequestException:
return "Request failed!"Utilisation de bibliothèques sécurisées :
from requests_toolbelt import sessions
def create_safe_session():
session = sessions.BaseUrlSession()
session.mount('file://', None) # Block file:// URLs
session.mount('ftp://', None) # Block FTP
return session
@app.route('/fetch', methods=['POST'])
def fetch_url():
url = request.form['url']
session = create_safe_session()
response = session.get(url)
return response.content| Catégorie | Information |
|---|---|
| TTP | Forcer le serveur à effectuer des requêtes vers des destinations arbitraires pour accéder à des ressources internes |
| CWE | CWE-918 (Server-Side Request Forgery (SSRF)) |
| Description de l'attaque | SSRF se produit lorsqu'une application web effectue des requêtes HTTP vers des URLs contrôlées par l'attaquant, permettant l'accès à des ressources internes, la reconnaissance de réseau, et potentiellement l'exécution d'actions non autorisées sur des services internes. |
| Impacts potentiels | - Accès aux métadonnées cloud (AWS, Azure, GCP) - Scan de ports internes et reconnaissance réseau - Accès à des services internes non exposés - Exfiltration de données depuis des APIs internes - Contournement de pare-feu et restrictions réseau |
| Comment la détecter | - Analyse de code pour les requêtes HTTP basées sur des entrées utilisateur - Tests de pénétration avec des URLs internes et de métadonnées - Monitoring des requêtes sortantes anormales - Utilisation d'outils de scan spécialisés (SSRFmap, etc.) |
| Remédiations/mitigations | - Valider et filtrer toutes les URLs d'entrée - Utiliser des listes blanches de domaines autorisés - Bloquer l'accès aux plages d'adresses privées et localhost - Implémenter des timeouts et limitations de requêtes - Utiliser des proxies dédiés pour les requêtes externes - Isolation réseau des services sensibles |
| Lien de référence | OWASP - Server-Side Request Forgery (SSRF) |