当前位置:首页 > 综合

目录遍历漏洞如何修复

目录遍历漏洞的目录修复,核心就一句话:**永远不要信任用户输入的遍历路径,服务器端必须自己决定文件访问的漏洞边界。** 但这句话落到实处,何修坑比想象中多。目录

我见过最典型的遍历错误代码长这样:

String filePath = "/var/www/files/" + request.getParameter("filename"); File file = new File(filePath); 

攻击者把`filename`参数改成`../../etc/passwd`,路径拼接后直接越权读取系统文件。漏洞这种代码在CTF靶场里一抓一大把,何修但在真实生产环境里也绝非罕见——去年某电商平台的目录安全巡检中,我就在他们的遍历图片下载接口里发现了同样的写法,当时冷汗就下来了。漏洞

修复的何修第一层,也是目录最容易被忽略的一层,是遍历**规范化路径**。用`File.getCanonicalPath()`或者`Path.normalize()`把路径里的漏洞`..`和`.`解析掉,然后检查解析后的路径是否以允许的根目录开头。注意,字符串替换`../`为空是没用的,攻击者可以构造`....//`或者URL编码绕过。正确姿势是:

String baseDir = "/var/www/files/"; String filename = request.getParameter("filename"); File file = new File(baseDir, filename); String canonicalPath = file.getCanonicalPath(); if (!canonicalPath.startsWith(new File(baseDir).getCanonicalPath())) {  throw new SecurityException("非法路径"); } 

但别高兴太早,光做路径校验还不够。我遇到过一种情况:应用部署在Windows服务器上,攻击者用`..%5c..%5c`(反斜杠的URL编码)绕过校验,因为Java在Windows下会把反斜杠也当作路径分隔符。所以**统一分隔符**是第二道保险,把传入参数里的`\`全部替换成`/`再做规范化,同时服务端代码里尽量用`Paths.get()`而不是手工拼字符串。

再往深一层说,路径拼接本身就是个反模式。更稳妥的方案是**用文件ID或数据库主键代替文件名**。比如用户上传一个文档,系统分配一个UUID,实际存储名是`a3f9c2e1-8b4d-4f7a-9e6c-1d2b3c4d5e6f.pdf`,用户请求时传这个UUID,服务端查表得到真实存储路径。这样做的好处是,即使用户传了`../../`,服务端根本不去拼路径,而是去查数据库——查不到就返回404,攻击面直接归零。这个方案我在一个政务系统里落地过,从那以后那个接口再没报过漏洞。

顺带提一嘴,很多开发者以为做了权限校验就万事大吉,但目录遍历漏洞的本质是**访问控制缺失**,而不是简单的输入校验问题。就算你限制了只能访问`/var/www/files/`目录,如果这个目录下有用户A的隐私文件,而用户B能猜到文件名,照样能拖走数据。所以文件下载接口必须做**用户级授权检查**,确认当前登录用户对该文件有访问权限。这事我吃过亏——早年间写过一个文件预览功能,只校验了路径合法性,没校验文件归属,结果被同租户的其他用户把合同文档全翻出来了,被领导骂了个狗血淋头。

最后补一刀:别忘了**错误信息泄露**。默认情况下,文件不存在时Java会抛出`FileNotFoundException`,而路径非法时抛出`SecurityException`,这两种异常信息直接返回给前端,等于告诉攻击者“你猜对了路径结构但没权限”。统一捕获异常,对外只返回一个模糊的“文件不存在”,日志里才记录详细原因。这一条不算目录遍历的直接修复,但能显著降低攻击者探测路径的效率。

说白了,修复目录遍历漏洞没有银弹,靠的是层层设防——规范化路径、避免直接拼接、用间接引用、加权限校验、模糊错误提示。每多一层,攻击者就得绕一个弯,成本上去了,漏洞自然就少了。别嫌麻烦,这活儿真出事的时候,代价比写代码时多花的那半小时贵多了。

分享到: