Vulnerabilities/Path Traversal
Path Traversal
Path traversal happens when an application builds a file path from request data and the operating system follows ../ segments, absolute names or symbolic links out of the folder the developer intended. Every language's path-join function keeps those segments, and string prefix checks are easy to get wrong. The fix is the same everywhere: resolve the final path, check containment by path component against the resolved base, or better, open files through an API that the kernel confines, and extract archives with a library that rejects unsafe member names. Pick your language below for the exact APIs, vulnerable and fixed code, and the linters that catch it.
Plain English Explainer (ELI5)
A records clerk may only fetch papers from your folder in the Customers cabinet. You hand in a slip that says: 'Start in my folder, step back out of it, step out of the Customers cabinet, go into Payroll and bring the salary sheet.' The clerk follows each step literally and never checks where the walk ends, so you get Payroll's file. A file name like ../../etc/passwd is that slip: each ../ means 'step out one folder'. The fix is for the clerk to work out the final location first and refuse any slip that ends outside your folder.
Core Concepts & Subsystem Terms
- Path traversal (CWE-22)
- An application uses untrusted input to build a file-system path and does not keep the result inside the intended directory, so a request can read or write files elsewhere with the application's permissions.
- Joining is not confining
os.path.join,path.join,filepath.Join,Path.Combine,File.joinandPath::joinonly glue strings together. They keep..segments, and several of them discard the base entirely when the user's part is absolute.- Canonical path and containment check
- Resolve the combined path to its canonical form (no
., no.., links followed), then require that it starts with the resolved base plus a path separator, or compare path components. Comparing strings without the separator accepts sibling folders such asuploads-old. - Symbolic links and races
- A lexical check cannot see a symbolic link inside the folder that points elsewhere, and a check done with
realpathbeforeopencan be raced by swapping a link in between. Kernel-enforced APIs such asopenat2withRESOLVE_BENEATHor Go'sos.Rootclose both gaps. - Zip Slip
- Archive members named
../../app/configare written outside the extraction folder by code that joins them onto it. Snyk's 2018 research found this in many libraries; use extractors that reject such names (filter="data",unpack_in,ExtractToDirectory, libarchive's secure flags).
Step-by-Step Attack Flow
Request data reaches a file API
A file name from a query string, form field, API response or archive member is joined onto an upload, template or report directory.
The name leaves the intended folder
It contains ../ segments, is absolute, names a sibling folder with the same prefix, or reaches a symbolic link. The benign test value ../../etc/hostname illustrates the read case.
The operating system opens the resolved path
The kernel resolves every .. and link and opens whatever file the final path names, with the application's permissions.
The attacker reads or overwrites files
Reads expose configuration, keys and source code; writes can replace templates, scripts or libraries that the application later executes.
Path traversal guides by language
Engineering & System Hardening Checklist
- Run a path-injection rule in CI (Bandit B202, gosec G304/G305, CodeQL
path-injectionandzipslipqueries, Psalm TaintedFile, Brakeman File Access) and fail on new findings. - Route every user-influenced path through one helper that resolves it and checks containment by component or with a trailing separator.
- Prefer kernel-enforced or framework-provided confinement:
os.Root,openat2(RESOLVE_BENEATH),send_from_directory,PhysicalFileProvider,sendFilewithroot. - Extract archives only with APIs that reject
.., absolute names and links, and never overwrite existing files during extraction. - Store uploads under generated IDs instead of client-supplied names, and never serve or write inside the application's code or configuration directories.
- Run the service with write access only to its own data folders so a missed check cannot replace code.