flawopen.com/Path traversal/Python os.path.join()
O os.path.join() do Python impede path traversal?
Não. Imagine a base como uma sala: .. atravessa salas parentais; um caminho absoluto é um novo endereço que faz os.path.join() descartar a sala inicial. Resolva o caminho final e confirme que permanece na pasta permitida antes de abrir o arquivo.
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
Dois comportamentos de junção a considerar
.. pode levar um caminho relativo para fora da pasta prevista. A documentação do Python diz que um segmento absoluto descarta os anteriores; no Windows, regras de unidade e raiz diferem do POSIX. pathlib.Path(base) / user_input também descarta os anteriores diante de um caminho absoluto. Junção e normalização não são autorização.
Por que commonpath() é mais seguro que prefixos
Resolva a base confiável e o candidato antes de compará-los. os.path.commonpath() compara componentes de caminho, não prefixos de caracteres, e lança ValueError para raízes ou unidades incompatíveis; rejeite esses casos. realpath() resolve links simbólicos existentes no momento da checagem, mas não evita sua troca antes da abertura. Se terceiros podem alterar a árvore, use acesso por descritor e controles no-follow da plataforma.
- ✓Rejeite entradas vazias ou que não sejam strings.
- ✓Resolva base e candidato; exija
commonpath((base, candidate)) == base. RejeiteValueErrore a própria base se for exigido um arquivo. - ✓Separe a contenção da autorização e permita somente arquivos e operações necessários ao endpoint.
- ✓Se usuários não confiáveis puderem alterar links simbólicos, evite corrida entre checagem e uso com APIs de descritor adequadas à plataforma.
Perguntas frequentes
Sim. O operador / de Path descarta componentes anteriores quando o próximo caminho é absoluto. Resolva o resultado e valide que está dentro da base permitida.
Não. Ele resolve links presentes durante a validação; outro processo pode alterá-los antes da abertura. Restrinja gravações na árvore ou use operações relativas a descritores com no-follow quando disponíveis.