flawopen.com/path-traversal/Java
How unvalidated File constructors in Java allow traversal, and how to verify boundaries using getCanonicalFile and Path.startsWith.
ホテルの客室カードリーダーが2階の部屋のみを開けるよう制限されている場面を例えに考えてみてください。もし宿泊客がドアのキーパッドに「../../master-safe」と入力すると、不備のある鍵が廊下を抜け出して支配人の金庫を直接解錠してしまいます。
Web Application SecurityCWE-918.CWE-918CWE-918):Standard Common Weakness Enumeration classification for path-traversal-java.Defense-in-DepthエンドポイントがHTTPリクエストパラメータからユーザー指定のファイル名やパスを受け取ります。
攻撃者が '../' や '..%2f' などの相対パス記号や絶対パス指定を挿入します。
バックエンドが正規化パスの検証を行わずに文字列を連結し、公開フォルダ外へのアクセスを許容します。
OSが /etc/passwd や設定ファイルなどの重要ファイルを読み取り、攻撃者へ返却します。
// 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() on both target and base directory。target.toPath().startsWith(base.toPath()) を厳格に検証してください。target.isFile() to prevent reading device nodes or directories を厳格に検証してください。ZipEntry.getName() before writing。