flawopen.com/path-traversal/Python
How os.path.join silently discards the root directory when input starts with a slash, and how to use pathlib.Path.resolve() and is_relative_to().
Imaginez un lecteur de carte d'hôtel programmé pour n'ouvrir que les chambres du 2e étage. Si un client tape '../../master-safe' sur le digicode, la serrure vulnérable remonte le couloir et déverrouille le coffre-fort principal du gérant.
Web Application SecurityCWE-918.CWE-918Defense-in-DepthUn point de terminaison accepte un nom de fichier ou un identifiant de ressource via un paramètre HTTP.
L'attaquant injecte des séquences relatives comme '../', '..%2f' ou des chemins absolus non autorisés.
Le backend concatène naïvement le fichier sans vérifier le chemin canonique ni restreindre le répertoire racine.
Le runtime ouvre et renvoie des fichiers système critiques (/etc/passwd, secrets d'API) à l'attaquant.
# VULNERABLE: os.path.join discards BASE_DIR if filename starts with '/'
import os
from flask import Flask, request, abort, send_file
app = Flask(__name__)
BASE_DIR = '/var/app/public/user_uploads'
@app.route('/download')
def download_file():
filename = request.args.get('file')
# If filename is "/etc/passwd", os.path.join returns "/etc/passwd"!
# If filename is "../../etc/passwd", it traverses outside BASE_DIR
target_path = os.path.join(BASE_DIR, filename)
if not os.path.exists(target_path):
abort(404)
return send_file(target_path)
# HARDENED: Resolve canonical path and enforce strict boundary containment
from pathlib import Path
from flask import Flask, request, abort, send_file
app = Flask(__name__)
BASE_DIR = Path('/var/app/public/user_uploads').resolve()
@app.route('/download')
def download_file():
filename = request.args.get('file', '')
# 1. Strip leading slashes and resolve relative components
clean_name = filename.lstrip('/\\')
target_path = (BASE_DIR / clean_name).resolve()
# 2. Strict containment check: target MUST be inside BASE_DIR
if not target_path.is_relative_to(BASE_DIR) or not target_path.is_file():
abort(403)
return send_file(target_path)
pathlib.Path.resolve() rather than os.path.abspath().