当前位置:首页 > 探索

路径遍历漏洞 java解决办法

一个下午,径漏洞我盯着某政府网站的遍历办法漏洞报告,攻击者只用了一个`../../../../etc/passwd`就拿到了系统文件列表。解决不是径漏洞没做过滤,是遍历办法过滤规则被绕过了——他们用URL编码把`../`变成了`%2e%2e%2f`,服务端没解码就放行了。解决这类问题在Java里尤其常见,径漏洞因为`File`类和`Path`类天生信任输入路径。遍历办法

最直接的解决解法是规范化路径后再校验。用`Paths.get(baseDir,径漏洞 userInput).normalize()`,把`..`和`.`都解析掉,遍历办法然后检查结果是解决否以`baseDir`开头。注意,径漏洞这个`startsWith`不能简单用字符串比较,遍历办法得用`Path.startsWith(Path)`方法,解决否则`/foo/bar2`会被`/foo/bar`误判为合法。我自己踩过这个坑,当时线上有个文件下载功能,用户传`/files/../../etc/passwd`,字符串前缀检查完全失效。

但光有规范化还不够。攻击者可能用绝对路径直接跳过拼接,比如传`/etc/passwd`而非相对路径。所以第二步,必须强制将拼接后的路径重新解析为相对路径,或者干脆拒绝所有以根目录开头的输入。更稳妥的做法是使用`Path.toRealPath()`,它不光规范化,还会解析符号链接——很多部署环境里,`/tmp`可能是指向其他目录的软链。

有些代码喜欢用`FilenameUtils.getName()`直接取文件名,觉得这样就能剥离路径。这想法太天真。Windows下的`C:\foo\bar.txt`在Linux上解析会变成完整文件名,而且反斜杠在Java字符串里本身就是转义符,处理不当直接报错或产生意外路径。我见过一个上传功能,只取了文件名,结果用户传`..\..\shell.jsp`,在Windows服务器上照样写入了上级目录。

真正该做的是两层防护。第一层,在接收参数时就校验字符集,禁止`..`、空字节、反斜杠等危险序列。第二层,在文件操作前用`File.getCanonicalPath()`做最终确认。这两层之间不要用同一个解析逻辑,否则攻击者找到规律就能同时绕过。例如,第一层用正则过滤,第二层用Path API,两者处理编码的方式不同,绕过的成本就高很多。

还有个小细节容易被忽略——临时文件。如果代码里用了`File.createTempFile()`,返回的路径是系统临时目录,但攻击者可能通过修改`java.io.tmpdir`系统属性来劫持。虽然这需要JVM本地权限,但配合其他漏洞就麻烦了。所以临时文件目录最好硬编码在配置里,不要从系统属性读取。

最后说个反面案例。某电商平台的文件导出接口,开发用了`new File(request.getParameter("filename"))`,然后直接`FileInputStream`读取。他们以为加了`/safe/`前缀就安全,但攻击者传`filename=/safe/../../etc/passwd`,前缀被`..`抵消了。修复的时候,他们只加了`replace("../", "")`,结果攻击者传`....//`,过滤后变成`../`,照样绕过。这种补丁式修法永远追不上攻击者的变形技巧。

说到底,路径遍历的根因是“隐式信任输入”。Java的`NIO.2` API提供了`Path`和`Files`,但用不对跟用`File`没区别。每次写文件操作代码前,先问自己:这个路径是否可能被用户控制?如果是,就别嫌麻烦,规范化、校验、再校验,三步缺一不可。

分享到: