Description
Path traversal occurs when an application uses input as a file path without enforcing the allowed directory boundary. An attacker can use relative components such as ../ or ..\\ to reach files outside that boundary.
Potential impact
The effects depend on the application's file permissions and read/write operations.
- Disclosure of system files or another user's data
- Unauthorized reading or modification of data and configuration
- Code execution if the application executes an attacker-controlled file
Remediation
- Avoid using input directly as a path. Validate it and restrict access to approved directories.
- Combine the path with
Path.resolve(), normalize it and compare thePathcomponents with the base path.resolve()alone does not enforce confinement. For existing files, usetoRealPath()to resolve symbolic links and check the actual destination as well. - If an attacker can modify directories between validation and use, restrict directory permissions and consider directory-relative APIs such as
SecureDirectoryStream. - Generate server-owned filenames, such as UUID-based names, where users do not need to choose a path.
Examples
Before
private static final String BASE_PATH ="/restricted/directory";
public String readFile(String filePath) throws IOException {
Path file = Paths.get("/restricted/directory/" + filePath);
return new String(Files.readAllBytes(file));
}
The second argument to File(parent, child) is also part of the path. Even with a fixed parent, .. components in an untrusted child can escape the base directory when the path is normalized.
import java.io.File;
import javax.servlet.http.HttpServletRequest;
public final class UnsafeUploadPath {
private static final File UPLOAD_DIR = new File("/var/app/uploads");
public File destination(HttpServletRequest request) {
String child = request.getParameter("name");
// Unsafe: the second constructor argument is also an attacker-controlled path.
return new File(UPLOAD_DIR, child);
}
}
After
This Java 11 or later example assumes attackers cannot modify the base directory or paths inside it. Apply file-size limits and appropriate responses to invalid input separately.
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
public final class SafeFileReader {
private static final Path BASE_PATH = Path.of("/restricted/directory");
public String readFile(String filePath) throws IOException {
Path base = BASE_PATH.toRealPath();
Path supplied = Path.of(filePath);
if (supplied.isAbsolute()) {
throw new SecurityException("Absolute paths are not allowed.");
}
Path candidate = base.resolve(supplied).normalize();
if (!candidate.startsWith(base)) {
throw new SecurityException("Unauthorized access attempt detected.");
}
// Resolve existing symbolic links and require the actual target to remain inside the base directory.
Path requestedPath = candidate.toRealPath();
if (!requestedPath.startsWith(base)
|| !Files.isRegularFile(requestedPath, LinkOption.NOFOLLOW_LINKS)) {
throw new SecurityException("Unauthorized access attempt detected.");
}
return Files.readString(requestedPath, StandardCharsets.UTF_8);
}
}
The first examples use untrusted paths directly, including new File(parent, child). The revised example rejects absolute paths, compares normalized path components with Path.startsWith(Path), resolves links and checks the actual destination. It reads the checked requestedPath itself.
Related CVEs
- CVE-2024-37032: Unvalidated registry digest permits path traversal (CWE-1287/CWE-23, Probllama)
- CVE-2024-4315: Windows path-separator omission permits traversal and file deletion (CWE-184/CWE-22)
- CVE-2022-45918: Unvalidated debugger log path permits traversal (CWE-73/CWE-20/CWE-24)