flawopen.com/Path Traversal/Java
You ask for document 'invoice_102.pdf'. If someone passes 'invoice_102.pdf/../../../../etc/passwd', the Java File object walks back up into root system files unless you force it to resolve its canonical path first.
In Java, new File(BASE_DIR, userInput) does not restrict files to BASE_DIR. An attacker submitting ../../../../etc/passwd causes the runtime to traverse outside the base path.
Landmark vulnerabilities in enterprise Java software (including Jenkins, Apache Solr, and Jira plugins) have suffered arbitrary file read and write vulnerabilities via Java path traversal.
CISA Cybersecurity Advisory & MITRE CVE repository.// VULNERABLE: Direct File instantiation with user parameter
import java.io.File;
import javax.servlet.http.*;
public class FileServlet extends HttpServlet {
private static final File BASE_DIR = new File("/var/app/public/files");
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String filename = req.getParameter("file");
// Attacker sends: ../../../../etc/passwd
File target = new File(BASE_DIR, filename);
// Directly serves arbitrary system file!
serveFile(target, resp);
}
}
// HARDENED: Canonicalize file and enforce directory prefix check
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import javax.servlet.http.*;
public class FileServlet extends HttpServlet {
private static final File BASE_DIR = new File("/var/app/public/files");
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String filename = req.getParameter("file");
if (filename == null || filename.isBlank()) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// 1. Resolve canonical path (resolves .. and all symlinks)
File target = new File(BASE_DIR, filename).getCanonicalFile();
File canonicalBase = BASE_DIR.getCanonicalFile();
// 2. Strict boundary check using Java NIO Path
if (!target.toPath().startsWith(canonicalBase.toPath())) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Path traversal detected");
return;
}
if (!target.isFile()) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
serveFile(target, resp);
}
}
getCanonicalFile() resolves all symbolic links and dot-dot sequences into a definitive system path. target.toPath().startsWith(canonicalBase.toPath()) strictly ensures the resolved file resides inside the allowed directory hierarchy.
getAbsolutePath() leaves '..' intact on non-existing paths; getCanonicalPath() resolves dots and symlinks via filesystem queries. Always use getCanonicalPath().
While Apache Commons FilenameUtils.getName() strips directory prefixes, it can prevent legitimate subfolder access within an allowed tree. Canonical checking is more flexible and robust.
spotbugs -textui -include spotbugs-security.xml .
getCanonicalFile() on both target and base directorytarget.toPath().startsWith(base.toPath())target.isFile() to prevent reading device nodes or directoriesZipEntry.getName() before writingJava's java.util.zip.ZipInputStream does not sanitize entry names. If a zip entry is named ../../evil.jsp, extract loops will write outside the target directory unless canonical checks are performed.