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

Is Python's os.path.join() safe from path traversal?

Reference page — draft, pending review
Short answer

No, and there's a second Python-specific trap: if the second argument is an absolute path, os.path.join() discards the first argument entirely, which is an easy way to accidentally bypass an intended base directory completely.

SURPRISING BEHAVIOR
os.path.join("/var/uploads", "/etc/passwd")
# returns "/etc/passwd" — the base
# directory is silently discarded
FIXED
full = os.path.realpath(
  os.path.join(base_dir, user_filename)
)
if not full.startswith(
  os.path.realpath(base_dir) + os.sep
):
    raise ValueError("Invalid path")

Two separate traps, not one

../ sequences let a relative filename walk upward out of the intended directory, the same issue as in every language. Python's os.path.join() adds a second, distinct trap: if the untrusted value is itself an absolute path (starting with /), join() ignores every argument before it — a filename of /etc/passwd passed as the "filename" completely replaces the intended base directory, with no traversal sequence involved at all.

FAQ

Does Path (pathlib) behave the same way?

Yes — Path(base) / user_input has the identical absolute-path-discards-base behavior as os.path.join(). The same resolved-path verification is needed regardless of which API builds the path.

References