flawopen.com/Path traversal/Python os.path.join()
os.path.join() empêche-t-il le path traversal ?
Non. Imaginez la base comme une pièce : .. traverse les pièces parentes ; un chemin absolu est une nouvelle adresse qui fait abandonner la pièce initiale à os.path.join(). Résolvez le chemin final et vérifiez qu’il reste dans le répertoire autorisé avant de l’ouvrir.
import os base_dir = "/srv/uploads" user_filename = "../../outside/demo.txt" path = os.path.join(base_dir, user_filename) # Relative traversal can leave the base. path = os.path.join(base_dir, "/outside/demo.txt") # An absolute segment discards base_dir.
import os
def resolve_under_base(base_dir, user_filename):
if not isinstance(user_filename, str) or not user_filename:
raise ValueError("Invalid filename")
base = os.path.realpath(base_dir)
candidate = os.path.realpath(
os.path.join(base, user_filename)
)
try:
inside = os.path.commonpath((base, candidate)) == base
except ValueError: # e.g. different Windows drives
inside = False
if not inside or candidate == base:
raise ValueError("Invalid filename")
return candidate
Deux comportements de jonction à prendre en compte
.. peut faire sortir un chemin relatif du répertoire prévu. La documentation Python précise qu’un chemin absolu fait ignorer les composants précédents ; sous Windows, les règles de lecteur et de racine diffèrent de POSIX. pathlib.Path(base) / user_input agit pareil. Ni jonction ni normalisation ne vérifie l’autorisation.
Pourquoi commonpath() est plus sûr que les préfixes
Résolvez la base de confiance et le candidat avant comparaison. os.path.commonpath() compare les composants, pas les préfixes de caractères, et lève ValueError si lecteurs ou racines sont incompatibles ; rejetez ces cas. realpath() résout les liens présents au contrôle, mais ne bloque pas leur remplacement avant l’ouverture. Si des tiers peuvent modifier l’arborescence, utilisez des descripteurs et des contrôles no-follow adaptés.
- ✓Rejetez les entrées vides ou non textuelles.
- ✓Résolvez base et candidat ; exigez
commonpath((base, candidate)) == base. RejetezValueErroret la base elle-même si un fichier est requis. - ✓Séparez confinement et autorisation ; limitez-vous aux fichiers et opérations nécessaires.
- ✓Si des utilisateurs non fiables modifient les liens symboliques, évitez les courses contrôle/utilisation avec des API de descripteur adaptées.
Questions fréquentes
Oui. L’opérateur / de Path ignore les composants précédents si le chemin suivant est absolu. Résolvez le résultat et vérifiez qu’il reste dans la base autorisée.
Non. Il résout les liens présents lors de la validation ; un autre processus peut les modifier avant l’ouverture. Limitez les écritures ou utilisez des opérations relatives à un descripteur avec no-follow si disponible.