← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Path Traversal

Security CWE-22 OWASP A3:2021 CVSS 7.5 PHP 5.0+ Intermediate
debt(d5/e3/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and psalm — both specialist SAST tools that can identify unsanitised user input flowing into file functions. A default linter won't catch it, and it won't surface as a compile error, but targeted static analysis rules (e.g. semgrep patterns matching fopen/readfile with $_GET/$_POST) can flag the common case. Not quite d7 because automated detection is explicitly marked 'yes' in the metadata.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is a small, focused change: pipe user input through realpath() and compare against the allowed base directory with str_starts_with(). This is more than a one-line swap because each vulnerable call site must also have the validation wrapper added and the base path defined, but it stays within a single component or utility function per file — no cross-cutting architectural rework needed.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). The applies_to scope covers web and cli contexts in PHP, but the fix is a localised validation pattern at file-access call sites. Once a safe file-access helper is extracted, the rest of the codebase is largely unaffected. It does not impose a strong gravitational pull on unrelated components.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field directly describes the trap: developers believe naive string-stripping (str_replace('../', '')) prevents path traversal, but attackers use encoded variants (..%2F, %2e%2e%2f, ....//), unicode sequences, and double-encoding that survive the replacement. This is a well-documented gotcha that contradicts the intuition that 'remove the bad string = safe', making it a serious cognitive trap one step below catastrophic.

About DEBT scoring →

Also Known As

directory traversal dot-dot-slash ../ attack path traversal attack

TL;DR

User input used in a file path allows attackers to navigate outside the intended directory using ../ sequences.

Explanation

Path traversal (also called directory traversal) lets attackers read or include arbitrary files on the server by supplying sequences like ../../etc/passwd in a parameter used to build a file path. In PHP, include/require with user-supplied filenames is the classic vector. Mitigation requires resolving the real path with realpath() and asserting it starts with the intended base directory — whitelist known-good filenames where possible.

How It's Exploited

GET /download?file=../../etc/passwd
# Reads /etc/passwd if not validated

Diagram

flowchart TD
    INPUT2[User input: ../../../etc/passwd] --> APP_READ[App reads<br/>uploads . . / . . / . . /etc/passwd]
    APP_READ --> SECRET[Reads /etc/passwd!]
    subgraph Fix2
        REALPATH2[realpath resolves all .. and symlinks]
        BASEDIR[Check resolved path starts with allowed dir]
        REALPATH2 --> BASEDIR
        BASEDIR -->|outside allowed| REJECT3[Reject - path traversal]
        BASEDIR -->|inside allowed| ALLOW2[Allow access]
    end
style APP_READ fill:#f85149,color:#fff
style SECRET fill:#f85149,color:#fff
style REJECT3 fill:#238636,color:#fff

Common Misconception

Stripping ../ from user input prevents path traversal. Attackers use encoded variants (..%2F, %2e%2e%2f, ....//), unicode sequences, and URL double-encoding that survive naive string replacement. Use realpath() and compare against the allowed base directory.

Why It Matters

A path traversal vulnerability can read any file the web server process has permission to access — including /etc/passwd, .env files, and PHP source containing database credentials.

Common Mistakes

  • Passing user-supplied filenames to file_get_contents(), readfile(), or fopen() without normalisation.
  • Filtering traversal sequences with str_replace('../', '') — bypassed with ....// which collapses to ../.
  • Not verifying that the realpath() of the requested file is within the intended directory.
  • Allowing the full filesystem path to be specified via a 'file' or 'path' query parameter.

Code Examples

✗ Vulnerable
$file = $_GET['file'];
readfile('/var/www/uploads/' . $file);
✓ Fixed
$base    = realpath('/var/www/uploads');
$requested = realpath($base . '/' . $_GET['file']);

if ($requested === false || !str_starts_with($requested, $base . DIRECTORY_SEPARATOR)) {
    http_response_code(403); exit;
}
readfile($requested);

Added 15 Mar 2026
Edited 22 Mar 2026
Views 79
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 3 pings W 3 pings T 5 pings F 2 pings S 5 pings S 4 pings M 2 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Scrapy 22 Amazonbot 7 Perplexity 7 Ahrefs 5 SEMrush 5 ChatGPT 4 Google 3 Unknown AI 2 Claude 2 Bing 2 Qwen 2 Majestic 1 Meta AI 1 Sogou 1 PetalBot 1
crawler 61 crawler_json 4
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Low
⚡ Quick Fix
Pass user input through realpath() then verify the result starts with your allowed base directory using str_starts_with()
📦 Applies To
PHP 5.0+ web cli
🔗 Prerequisites
🔍 Detection Hints
readfile( or fopen( or include( or file_get_contents( with $_GET/$_POST without realpath validation
Auto-detectable: ✓ Yes semgrep psalm
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-22 CWE-23 CWE-35


✓ schema.org compliant