FlawOpen

flawopen.com/Path traversal/Python os.path.join()

O os.path.join() do Python impede path traversal?

CWE-22: Path TraversalPágina de referência
Resposta curta

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.

VULNERABLE
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.
SECURE — resolve and check containment
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.

Perguntas frequentes

pathlib se comporta da mesma forma?

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.

realpath() elimina condições de corrida?

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.

Referências

Veja também Path traversal (CWE-22): como funciona e como evitar em todas as linguagens — flawopen.com O que é uma vulnerabilidade Zip Slip? Node.js path.join() não impede Path Traversal PHP include() e caminhos controlados pelo usuário