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().
Imagine um scanner de chave de hotel que só deveria abrir quartos no segundo andar. Se um hóspede digita '../../master-safe' no teclado da porta, uma fechadura vulnerável sobe pelo corredor e abre o cofre principal da gerência.
Web Application SecurityCWE-918.CWE-918Defense-in-DepthUm endpoint aceita um nome de arquivo ou identificador de recurso fornecido pelo usuário por parâmetro HTTP.
O invasor injeta sequências de diretório relativo como '../', '..%2f' ou caminhos absolutos no parâmetro.
O backend concatena o nome sem resolver o caminho canônico nem validar que o destino permaneça no diretório base.
O runtime abre e transmite arquivos sensíveis do sistema (como /etc/passwd ou credenciais) diretamente ao cliente.
# 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().