flawopen.com/Path traversal/Python os.path.join()
¿os.path.join() de Python evita el path traversal?
No. Imagina la base como una habitación: .. atraviesa habitaciones principales; una ruta absoluta es una dirección nueva que hace que os.path.join() descarte la habitación inicial. Resuelve la ruta final y confirma que siga dentro del directorio permitido antes de abrirla.
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
Dos comportamientos de unión que debes considerar
.. puede sacar una ruta relativa de su directorio previsto. La documentación de Python indica que una ruta absoluta descarta los componentes anteriores; en Windows, las reglas de unidad y raíz difieren de POSIX. pathlib.Path(base) / user_input también descarta lo anterior ante una ruta absoluta. Unir y normalizar no autoriza el acceso.
Por qué commonpath() es más seguro que los prefijos
Resuelve la base confiable y la ruta candidata antes de compararlas. os.path.commonpath() compara componentes, no prefijos de caracteres, y lanza ValueError ante raíces o unidades incompatibles; rechaza esos casos. realpath() resuelve enlaces simbólicos existentes al comprobar, pero no evita que cambien antes de abrir el archivo. Si terceros pueden modificar el árbol, usa descriptores y controles no-follow adecuados a la plataforma.
- ✓Rechaza entradas vacías o que no sean cadenas.
- ✓Resuelve base y candidata; exige
commonpath((base, candidate)) == base. RechazaValueErrory la propia base si necesitas un archivo. - ✓Separa contención y autorización; permite solo los archivos y operaciones que necesita el endpoint.
- ✓Si usuarios no confiables pueden cambiar enlaces simbólicos, evita carreras entre comprobación y uso con APIs de descriptores apropiadas.
Preguntas frecuentes
Sí. El operador / de Path descarta componentes anteriores cuando la siguiente ruta es absoluta. Resuelve el resultado y verifica que esté dentro de la base permitida.
No. Resuelve enlaces existentes al validar; otro proceso puede cambiarlos antes de abrir el archivo. Restringe escrituras o usa operaciones relativas a descriptores con no-follow cuando estén disponibles.