Java目录遍历案例:从漏洞原理到安全防御实战指南
目录导读
什么是Java目录遍历漏洞
目录遍历(Directory Traversal),又称路径穿越漏洞,是指攻击者通过构造特殊文件路径(如../../../etc/passwd)突破Web应用的路径限制,访问或读取服务器上任意文件的一种安全漏洞,在Java Web应用中,该漏洞通常出现在文件下载、文件读取、图片预览等功能模块中。

核心特征:
- 利用或等跨级符号
- 绕过服务端的路径拼接或校验逻辑
- 目标包括配置文件、密码文件、源代码等敏感数据
问题1:目录遍历和任意文件读取是同一个漏洞吗?
答:不完全相同,目录遍历侧重于通过路径穿越访问目录列表,而任意文件读取更倾向于直接读取文件内容,但实践中两者常并发出现,安全防护策略也高度重叠。
目录遍历的底层原理与危害
1 漏洞产生的根本原因
Java Web应用在处理用户输入的文件名或路径时,未对特殊字符(如、、%2e%2e%2f)进行严格过滤,直接将用户输入拼接到文件系统路径中,导致可访问预期之外的文件。
典型错误代码:
String filename = request.getParameter("file");
String filePath = "/var/data/" + filename; // 直接拼接用户输入
File file = new File(filePath);
// 返回文件内容
当用户传入../../../etc/passwd,实际路径变为/var/data/../../../etc/passwd,即访问到/etc/passwd。
2 漏洞危害分级
| 危害等级 | 典型后果 | 影响范围 |
|---|---|---|
| 高危 | 读取数据库配置、SSH密钥 | 服务器沦陷 |
| 中危 | 读取日志、源码 | 信息泄露 |
| 低危 | 读取静态资源 | 影响有限 |
问题2:目录遍历能不能直接导致远程代码执行?
答:单纯目录遍历是文件读取漏洞,但可通过组合攻击实现RCE:例如读取/WEB-INF/web.xml获取框架配置,再读取class文件反编译,或通过读取jsp文件获取业务逻辑漏洞点,最终可能演变为代码执行。
典型高危案例复现(含代码)
文件下载功能中的目录遍历
场景:某文档管理系统提供PDF下载功能,URL格式为/download?file=report_2024.pdf
攻击Payload:
/download?file=../WEB-INF/web.xml
/download?file=../../../etc/shadow
Java漏洞代码(使用Apache Commons IO):
@GetMapping("/download")
public void downloadFile(String file, HttpServletResponse response) {
File f = new File(BASE_PATH + file); // BASE_PATH = "/app/files/"
InputStream is = new FileInputStream(f);
// ... 流复制到response
}
攻击结果:攻击者成功读取/etc/shadow文件,获取系统用户哈希。
URL编码绕过
攻击者可能使用URL编码绕过简单关键词过滤:
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd → 等价于 ../../../etc/passwd
..%252f..%252f..%252fetc%252fpasswd → 双重编码绕过
空字节截断(Java 8以下版本)
file=../../../etc/passwd%00.jpg
Java早期版本中,%00空字节会截断字符串处理,导致实际读取/etc/passwd。
问题3:为什么现代Java框架(如Spring Boot)仍然出现目录遍历?
答:框架本身如ResourceHttpRequestHandler已提供路径校验,但开发者自定义控制器时若自行处理getRealPath()或File()构造,仍会引入漏洞。框架安全不等于业务代码安全。
真实攻击场景与绕过技术
1 高级绕过手法
-
编码组合绕过
→ 使用分号截断(Tomcat解析特性)
→ 路径分隔符变体
→ Windows系统路径 -
绝对路径绕过
某些场景下直接传入绝对路径:
file=/etc/passwd→ 若代码仅检查前缀/app/files/,则可能绕过 -
Unicode规范化绕过
%c0%ae%c0%ae%c0%af→ 利用UTF-8解码漏洞(已修复于旧版IIS)
2 攻击链升级路径
目录遍历 → 读取数据库配置 → 数据库脱库
→ 读取SSL证书 → 中间人攻击
→ 读取OAuth密钥 → 账户接管
→ 读取JSP/JS源码 → 发现更多漏洞
企业级防御方案(附代码示例)
白名单校验(推荐)
private static final Set<String> ALLOWED_FILES = new HashSet<>(Arrays.asList(
"report_2024.pdf", "user_manual.pdf", "terms.pdf"
));
public boolean isAllowed(String fileName) {
return ALLOWED_FILES.contains(fileName);
}
规范化路径校验(通用方案)
public String sanitizePath(String userInput, String baseDir) {
File base = new File(baseDir).getCanonicalFile();
File target = new File(base, userInput).getCanonicalFile();
// 关键校验:目标路径必须在base路径内
if (!target.getPath().startsWith(base.getPath() + File.separator)) {
throw new SecurityException("Invalid file path");
}
return target.getPath();
}
关键点:必须使用getCanonicalPath()规范化所有和链接符号。
文件服务专用接口
使用Resource抽象,而非直接操作File:
@GetMapping("/download/{filename:.+}")
public ResponseEntity<Resource> download(@PathVariable String filename) {
Path file = Paths.get("data").resolve(filename).normalize();
if (!file.startsWith(Paths.get("data"))) {
throw new SecurityException();
}
Resource resource = new UrlResource(file.toUri());
return ResponseEntity.ok().body(resource);
}
Web应用防火墙(WAF)规则
- 拦截包含、
%2e%2e的请求 - 限制文件后缀白名单(如只允许
.pdf,.docx) - 使用RequestDispatcher过滤恶意路径
问题4:已经有WAF了,代码还需要防吗?
答:需要!WAF可被绕过(如分块传输、编码混淆),代码校验是最后一道防线,必须双管齐下。
常见问答与排错
Q1:如何快速检测Java应用是否存在目录遍历?
A:使用自动化扫描工具(如Burp Suite Intruder)发送以下Payload:
file=test.pdf
file=../test.pdf
file=..\..\..\..\..\windows\win.ini
file=../../../etc/passwd
观察响应长度、状态码、错误信息差异。
Q2:防御代码中为什么用getCanonicalPath()而不是getAbsolutePath()?
A:getAbsolutePath()仅解析绝对路径,不会消除,而getCanonicalPath()会解析符号链接和相对路径,得到真实文件路径。
Q3:Nginx反向代理能否防御目录遍历?
A:部分情况可以,通过在nginx配置中添加$uri限制,但业务代码依然需要防御,因为攻击者可直接请求应用端口。
构建纵深防御体系
通过本文的Java目录遍历案例解析,可以看到该漏洞虽然原理简单,但危害极大、绕过手段多样,建议采取以下防御策略:
- 核心防御:所有涉及文件读取的功能必须使用规范化的路径校验
- 补充措施:部署WAF、限制文件服务器权限、最小化文件读取路径
- 持续监控:日志审计中监控异常路径模式(如连续)
- 定期扫描:使用OWASP ZAP等工具对每个版本进行安全测试
记住一条铁律:永远不要信任用户输入的文件名,从第一个版本开始就写入正确的路径校验逻辑,比后期修补成本低100倍。