A directory traversal attack lets an attacker read files outside your web root using sequences like "../../../etc/passwd". This guide covers how these attacks work, real examples, and complete prevention strategies.
Anay Pandya
1,339 words
Anay Pandya
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
A directory traversal attack (also called path traversal) allows an attacker to access files and directories stored outside the intended web root folder by manipulating file path inputs with sequences like ../. This class of vulnerability is deceptively simple yet consistently appears in real-world breaches — reading /etc/passwd, configuration files with credentials, or application source code.
<div class="zf-stat-callout" style="background:#0d1117;border:1px solid rgba(16,185,129,0.25);border-left:3px solid #10b981;border-radius:4px;padding:16px 20px;margin:24px 0"> <p style="margin:0 0 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.15em;color:#10b981;font-family:monospace">ZeriFlow Data — 12,400+ sites analyzed</p> <p style="margin:0;font-size:13px;color:#e2e8f0;line-height:1.6;font-family:monospace">Across 12,400+ sites scanned on ZeriFlow, 19% expose sensitive paths such as /.git/, /backup, or /admin without — providing attackers a direct window into application internals.</p> </div>
Keep reading
Is your site actually secure?
Run a free check — 60 seconds
Check if your application exposes sensitive paths — [scan it free with ZeriFlow](https://zeriflow.com).
Web applications often use user-supplied input to construct file paths:
https://example.com/download?file=report.pdf
https://example.com/image?src=avatar.png
https://example.com/template?name=welcomeIf the application appends this input directly to a base path without validation:
# Vulnerable code
import os
def download_file(filename):
base_dir = '/var/www/uploads/'
file_path = base_dir + filename
return open(file_path, 'rb').read()An attacker passes ../../../../etc/passwd as the filename:
GET /download?file=../../../../etc/passwdThe resulting path is /var/www/uploads/../../../../etc/passwd, which resolves to /etc/passwd. The server happily returns the system's user account file.
WAFs and basic filters often check for ../ literally. Attackers bypass these with encoding:
| Variant | Encoded Form |
|---|---|
../ | %2e%2e%2f |
../ | ..%2f |
../ | %2e%2e/ |
.. (Windows) | ..\ → ..%5c |
Double-encoding is also common: %252e%252e%252f (where %25 is the encoding of %).
One of the most famous recent path traversal vulnerabilities. Apache 2.4.49 introduced a flaw where URL normalization could be bypassed:
GET /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd HTTP/1.1This was exploited in the wild within hours of disclosure. Patched in Apache 2.4.50 — but only after massive exploitation attempts.
A path traversal variant targeting archive extraction. A malicious ZIP file contains entries with paths like ../../../../etc/cron.d/malicious. When extracted, the file lands outside the intended directory and can achieve remote code execution if placed in an executable location.
While Log4Shell was primarily an RCE via JNDI injection, many affected systems also had path traversal vulnerabilities that allowed attackers to read application configuration files containing credentials used for lateral movement.
Always resolve the canonical (absolute) path of user-supplied input and verify it starts with your intended base directory:
import os
def safe_file_read(filename):
base_dir = '/var/www/uploads'
# Resolve to absolute canonical path
requested_path = os.path.realpath(os.path.join(base_dir, filename))
# Ensure the resolved path is within the base directory
if not requested_path.startswith(os.path.realpath(base_dir) + os.sep):
raise PermissionError('Path traversal attempt detected')
with open(requested_path, 'rb') as f:
return f.read()The key: os.path.realpath() resolves all .. sequences and symlinks before you check the prefix.
Java equivalent:
Path basePath = Paths.get('/var/www/uploads').toRealPath();
Path requestedPath = basePath.resolve(userInput).normalize();
if (!requestedPath.startsWith(basePath)) {
throw new SecurityException('Path traversal detected');
}Instead of accepting filenames directly, accept an index or identifier that maps to a file server-side:
FILE_MAP = {
'1': 'report_q1.pdf',
'2': 'report_q2.pdf',
'3': 'user_guide.pdf'
}
def download_file(file_id):
filename = FILE_MAP.get(file_id)
if not filename:
raise ValueError('Invalid file ID')
return open(f'/var/www/uploads/{filename}', 'rb').read()No user input ever touches the file system path. This is the most robust approach.
If you must accept filenames, validate extensions against a strict allowlist:
ALLOWED_EXTENSIONS = {'.pdf', '.png', '.jpg', '.csv'}
def is_safe_filename(filename):
_, ext = os.path.splitext(filename)
return ext.lower() in ALLOWED_EXTENSIONSThis doesn't prevent traversal alone but combined with path canonicalization it reduces the attack surface significantly.
Even if path traversal occurs, damage is limited if the web process can only read files it needs:
noexec if possible.chroot jails or seccomp to restrict syscalls.ZeriFlow checks for several indicators that may signal path traversal exposure:
Run a free ZeriFlow security scan to identify these misconfigurations before attackers do.
Path traversal on Windows has additional complexity:
..\..\windows\system32\drivers\etc\hostsC:\Windows\... bypass relative path checks.PROGRA~1 for Program Files, WINDOW~1 for Windows — these can bypass filename-based filters.Always use the platform's canonical path resolution API rather than string manipulation.
When processing uploaded archives:
import zipfile, os
def safe_extract(zip_path, extract_to):
with zipfile.ZipFile(zip_path) as zf:
for entry in zf.namelist():
# Resolve the target path
target_path = os.path.realpath(os.path.join(extract_to, entry))
real_extract_to = os.path.realpath(extract_to)
if not target_path.startswith(real_extract_to + os.sep):
raise ValueError(f'Zip Slip detected: {entry}')
zf.extract(entry, extract_to)Libraries like Python's zipfile don't prevent Zip Slip by default — you must validate every entry.
A: Directory traversal refers to accessing files outside the intended directory via path manipulation. Local File Inclusion (LFI) is a PHP-specific vulnerability where user input is passed to include() or require() — it often leverages path traversal techniques. LFI can be more dangerous as it may execute PHP code embedded in included files. Directory traversal is the broader technique; LFI is a specific implementation of it.
A: WAFs provide partial protection by blocking known traversal patterns, but sophisticated encodings and double-encoding attacks frequently bypass them. WAFs are a supplement, not a replacement for secure coding practices. Always fix the vulnerability in code; use WAF rules as a defence-in-depth layer.
A: Yes. Applications that construct object keys from user input can be vulnerable to equivalent attacks. For example, if an app stores users/{user_id}/{filename} and user_id isn't validated, an attacker might access users/../../admin/secrets. Always validate and sanitize cloud storage key components.
A: Manual testing: try appending ../../../etc/passwd (Linux) or ..\..\..\windows\win.ini (Windows) to any file-related parameter. Automated testing: tools like Burp Suite's active scanner, OWASP ZAP, or ZeriFlow can flag potential traversal exposure automatically.
A: Containers limit the blast radius — an attacker can only traverse within the container's filesystem. But containers typically contain application secrets, environment variables, and internal service credentials. Path traversal in a container can still lead to credential theft and lateral movement. Fix the vulnerability regardless.
Directory traversal is a consistently exploited vulnerability class — from high-profile Apache CVEs to supply chain attacks via Zip Slip. The fix is always the same: never trust user input when constructing file paths, use canonical path resolution before access checks, and prefer indirect file references over accepting raw filenames.
Start by understanding your exposure. [Scan your site with ZeriFlow](https://zeriflow.com) to check for directory listing, server version disclosure, and other indicators that make path traversal attacks easier. Free, instant, no account required.
<!-- zf-internal-links -->
Related resources
Website Vulnerability Scanner
Run a broader website security audit across headers, TLS, DNS, cookies, SEO, and disclosure checks.
Security Headers Checker
Check CSP, HSTS, X-Frame-Options, and other response headers.
SSL Checker
Review TLS certificate, HTTPS, and transport security signals.
DMARC Checker
Validate email authentication records for domain spoofing protection.
CSP Checker
Review Content-Security-Policy coverage and common gaps.
June 24, 2026
Best Security Tools for AI Startups
Monitor your security score
Schedule recurring scans and catch regressions after deployments.