Path Traversal Remediation
Risk Severity
๐ High Fix Effort
๐ง Medium Est. Time
โฑ๏ธ 2-4 hours Reference
A01:2021 CWE-22
Path traversal (directory traversal) attacks allow attackers to access files outside the intended directory using sequences like "../". This can lead to reading sensitive files, source code, or even code execution.
Impact
Successful path traversal can expose configuration files, credentials, source code, and system files.
Combined with file upload vulnerabilities, it can lead to remote code execution.
Understanding Path Traversal
Attack Examples
Basic traversal:
../../../etc/passwd ..\..\..\..\windows\win.ini Encoded variants:
%2e%2e%2f%2e%2e%2f ....//....//etc/passwd Remediation Strategies
1. Avoid User Input in Paths
Use database IDs or UUIDs that map to files instead of filenames.
2. Canonicalize and Validate
Resolve the full path, then verify it starts with the allowed base path.
3. Allowlist Approach
Only allow specific, predefined files or patterns.
4. Chroot/Sandbox
Run file operations in a restricted directory jail.
Python
python
import os
from pathlib import Path
UPLOAD_DIR = Path('/var/www/uploads').resolve()
def safe_file_access(user_filename):
"""Safely access a file within the upload directory."""
# Construct the full path
requested_path = (UPLOAD_DIR / user_filename).resolve()
# Verify path is within allowed directory
if not str(requested_path).startswith(str(UPLOAD_DIR)):
raise ValueError("Access denied: path traversal attempt")
# Additional check: file must exist
if not requested_path.is_file():
raise FileNotFoundError("File not found")
return requested_path
# Alternative: Use indirect references
FILE_MAPPING = {
'doc1': '/var/www/uploads/document1.pdf',
'doc2': '/var/www/uploads/document2.pdf',
}
def get_file_by_id(file_id):
if file_id not in FILE_MAPPING:
raise ValueError("Invalid file ID")
return FILE_MAPPING[file_id]Java
java
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
public class SafeFileAccess {
private static final String BASE_DIR = "/var/www/uploads";
public static File safeGetFile(String userInput) throws IOException {
// Resolve and canonicalize the path
Path basePath = Paths.get(BASE_DIR).toRealPath();
Path requestedPath = basePath.resolve(userInput).normalize().toRealPath();
// Verify path is within base directory
if (!requestedPath.startsWith(basePath)) {
throw new SecurityException("Path traversal attempt detected");
}
return requestedPath.toFile();
}
// Alternative: Sanitize filename (remove path components)
public static String sanitizeFilename(String filename) {
return new File(filename).getName(); // Returns only the filename
}
}Node.js
javascript
const path = require('path');
const fs = require('fs');
const BASE_DIR = '/var/www/uploads';
function safeFileAccess(userFilename) {
// Resolve to absolute path
const requestedPath = path.resolve(BASE_DIR, userFilename);
// Normalize and check if within base directory
const normalizedBase = path.normalize(BASE_DIR);
const normalizedPath = path.normalize(requestedPath);
if (!normalizedPath.startsWith(normalizedBase + path.sep)) {
throw new Error('Access denied: path traversal attempt');
}
// Additional validation
if (!fs.existsSync(requestedPath)) {
throw new Error('File not found');
}
return requestedPath;
}
// Alternative: Use basename to strip directory components
function sanitizeFilename(filename) {
return path.basename(filename);
}PHP
php
<?php
define('BASE_DIR', '/var/www/uploads');
function safeFileAccess($userFilename) {
// Build full path and resolve it
$requestedPath = realpath(BASE_DIR . DIRECTORY_SEPARATOR . $userFilename);
$basePath = realpath(BASE_DIR);
// realpath returns false if file doesn't exist
if ($requestedPath === false) {
throw new Exception('File not found');
}
// Verify path is within base directory
if (strpos($requestedPath, $basePath) !== 0) {
throw new Exception('Access denied: path traversal attempt');
}
return $requestedPath;
}
// Alternative: Strip directory from filename
function sanitizeFilename($filename) {
return basename($filename);
}
?>๐งช Testing Verification
Path Traversal Test Payloads
text
# Basic traversal - should be blocked
../../../etc/passwd
..\..\..\windows\win.ini
# URL encoded
%2e%2e%2f%2e%2e%2fetc%2fpasswd
%2e%2e%5c%2e%2e%5cwindows%5cwin.ini
# Double encoding
%252e%252e%252f
# Null byte (older systems)
../../../etc/passwd%00.jpg
# Unicode/overlong encoding
..%c0%af..%c0%af
# Path normalization bypass
....//....//etc/passwd
..;/..;/etc/passwd
# Expected: All should fail or return safe responseCommon Mistakes
Blocklist Approach
python
# DON'T: Easy to bypass
if "../" in filename:
reject()Bypassed with encoding or ....//
Correct Approach
python
# DO: Resolve and verify
real_path = Path(base / file).resolve()
if not str(real_path).startswith(base):
reject()Canonicalization handles all bypasses
Checking Before Canonicalization
python
# DON'T: Check raw input
if not raw_path.startswith(base):
reject()
# Then use raw_path...Path may resolve differently
Correct Approach
python
# DO: Canonicalize first
real = os.path.realpath(raw_path)
if not real.startswith(base):
reject()Check the actual resolved path
Path Traversal Prevention Checklist
- โ User input not used directly in file paths
- โ Path canonicalization before access checks
- โ Resolved path verified to be within base directory
- โ Filename sanitization (strip directory components)
- โ Indirect file references used where possible
- โ File system permissions properly configured