Web安全三大边界漏洞:目录遍历、越权与信息泄露的攻防实战
做Web安全这些年如果要我列一个“出现频率最高的漏洞Top 3”目录遍历、越权、信息泄露绝对榜上有名。它们没有SQL注入那么刺眼也不像RCE那样一锤定音但几乎每一次拿下的目标背后都站着这几类漏洞的影子。尤其是现在扫描器越来越普及开发同学收到报告时最常问我的三句话就是这个目录遍历到底怎么修的为什么接口都登录了还说越权那个什么CVE信息泄露是【原理扫描】到底要不要管所以这篇文章我不打算做教科书式的漏洞科普而是把这三类漏洞放到同一个工作台上拆开揉碎它们的共同底层逻辑、各自的成因和验证方法、以及我在实际项目中踩过坑之后沉淀的修复方案适合渗透测试新手、代码审计同学也适合天天被安全报告追着问的开发和运维朋友。1. 先想清楚一件事三类漏洞共用同一个边界模型1.1 人—系统—资源边界到底在哪里我习惯用一个三层楼的比喻来解释这三类漏洞。系统是一座楼用户是拿着门禁卡的人资源是楼里的房间、文件和保险柜。目录遍历相当于门禁系统失效你明明只能在一楼活动结果通过一个电梯按钮的Bug直接走到了地下室甚至机房把你本来不该看到的文件翻了个遍。越权则是门禁卡能刷开大部分门但系统在识别身份时出了问题——它把门禁卡上的编号当成了你的真实身份于是你可以冒充别人刷卡进门甚至混进管理员办公室。信息泄露更直接相当于楼里的墙上到处贴着内部图纸、保险柜密码和门禁系统源代码任何人都能瞄一眼。这个比喻虽然简单但它揭示了一个统一模型任何系统都由人请求方、系统处理逻辑、资源数据与功能三者构成安全边界就分布在这三者的交界处。目录遍历是“请求与文件系统”之间的边界失控越权是“身份与授权模型”之间的边界失控信息泄露是“系统输出与数据暴露面”之间的边界失控。理解了这一点修漏洞的思路就不会再停留于“见一个补一个”而是站在边界设计的层面去收敛问题。1.2 为什么“目录遍历、越权、信息泄露”总是组团出现很多扫描报告里这三类漏洞往往同时出现这不是巧合。从代码层面看它们的病根非常相似开发者默认信任了用户输入。文件名直接拼接路径是信任输入业务接口直接拿前端传的userId查询数据也是信任输入调试模式未关闭就上线、Actuator端点无鉴权暴露在公网本质还是“没想过有人会来访问这些不该被访问的地方”。从攻击层面看它们天然具备组合效应。目录遍历能帮你读到配置文件和代码信息泄露能帮你拿到密钥和Token越权能让你用这些凭据横向访问业务数据。单独看任何一个可能都只是“中危”“低危”但串起来就是一条能拿到核心数据的完整链路。所以我在做风险评估时从来不会孤立地看某个漏洞的CVSS分数而是看它能不能被当作链条上的一环利用。1.3 攻击者视角一条链路串起三个漏洞站在攻击者视角漏洞分类毫无意义只有“能不能让我走到下一步”才有意义。一个典型的攻击路径是这样的先在404页面或Swagger文档里发现系统信息信息泄露再通过一个文件下载接口读出application.yml和数据库配置目录遍历拿到数据库账号后试着登录后台最后在业务接口里替换用户ID访问别人订单越权。整个过程里三类漏洞各司其职信息泄露负责开视野目录遍历负责拿钥匙越权负责进金库。这也是我为什么建议安全团队在写报告时不要只罗列漏洞清单而是按攻击链把漏洞串起来描述。开发同学看到“目录遍历HeapDump信息泄露水平越权”这三个词时可能毫无感觉但如果告诉他“攻击者可以读出配置文件、下载堆内存、然后访问任意用户订单”他对修复优先级和紧急程度的理解会完全不一样。2. 目录遍历实战拆解路径拼接与编码绕过的坑2.1 先用一个真实请求理解目录遍历目录遍历Path Traversal的触发点往往藏在那些看起来人畜无害的功能里。比如一个文件下载接口GET /download?fileNamereport_2025.pdf HTTP/1.1 Host: example.com后端如果写成这样String baseDir /data/reports/; String filePath baseDir request.getParameter(fileName); // 直接用filePath读取文件返回给前端那fileName参数就完全可控了。把参数改成下面这样GET /download?fileName../../../../etc/passwd HTTP/1.1路径拼接后就变成了/data/reports/../../../../etc/passwd。操作系统解析路径时..表示往上一级目录跳一路向上最终跳出reports目录读到了系统里的/etc/passwd。如果这是在Windows环境还常见到..\..\..\windows\win.ini这样的写法。别小看这类漏洞我见过有测试系统通过目录遍历直接读到了打包在服务器上的源代码压缩包整个业务逻辑被翻了个底朝天。2.2 三个最常见的成因以及对应的绕过手法目录遍历的成因归纳起来基本逃不出三个坑。第一个坑是直接拼接用户输入没有任何校验这是我上面举例的那种情况。第二个坑是做了过滤但过滤得不彻底。比如后端只过滤了../字符串攻击者换成....//服务端把这个字符串里的../剥掉一层后剩下的恰好还是../完成了绕过。第三个坑是用了不安全的文件读取API却没有对最终解析后的路径做二次校验导致程序跑着跑着就跳出了预期目录。我整理了一张过滤绕过对照表日常测试时可以直接拿来当速查过滤方式常见绕过手法过滤../....//、..././、..////过滤单次编码..%2f、%2e%2e%2f、%252e%252e%252f双重编码过滤http://hthttp://tp://关键字双写使用正则过滤路径跳跃尝试绝对路径/etc/passwd、C:\Windows\win.ini依赖低版本Web容器%00空字节截断老版本Java/PHP环境为什么编码绕过能成功因为Web容器和框架经常会对URL做多次解码一层解完交给应用层应用层如果又做了一次解码就会造成两次解码不一致。我的建议是测试时优先试一次..%2f再试双重编码命中概率最高。2.3 黑盒验证从识别入口到确认风险实际做黑盒测试时第一步不是直接发Payload而是先识别可能存在的文件操作入口。优先关注这几类参数名file、fileName、path、dir、name、url、image、download。功能点上重点看文件下载、文件预览、缩略图生成、导入导出、附件读取。这些地方只要后端存在路径拼接多半逃不掉。第二步是发一个无害的探测包。我习惯先请求一个肯定存在的文件观察正常响应再请求一个不存在的文件名观察404错误特征目的是区分后端是返回了文件内容还是只返回了JSON提示。第三步才是发目录遍历Payload。命中之后的响应特征很典型返回的Content-Type变成了text/plain或application/octet-stream响应体里出现root:x:0:0:之类的内容。这里有一个易被忽略的细节有些接口把文件内容Base64编码后再返回所以即使看到一串Base64也要尝试解码看看是不是文件内容。测试时务必确认目标已经获得授权千万不要对着未授权的目标做验证这是行业的底线。2.4 修复方案白名单、路径归一化与安全API修复目录遍历我的核心思路是三个关键词白名单、归一化、安全API。先说白名单最简单粗暴且效果最好的方式如果业务上下载的文件名是可控的有限集合直接维护一个白名单映射表用户请求的是1后端映射为report_2025.pdf从源头杜绝用户输入进入文件系统。如果业务确实需要开放文件名那至少要保证路径归一化校验。以Java为例推荐的写法是Path basePath Paths.get(/data/reports).toRealPath(); Path targetPath Paths.get(basePath.toString(), fileName).normalize(); if (!targetPath.toRealPath().startsWith(basePath)) { throw new SecurityException(非法路径); }先对基础目录调用toRealPath()拿到真实规范路径再把用户输入拼接后做normalize()归一化最后用startsWith确认目标路径仍在基础目录内部。这样即使攻击者传了..归一化后也会因为跳出了基础目录而被拦截。最后能用安全API就尽量不要自己拼路径比如Java里优先用Files.newInputStream(Path)、Python里优先用os.path.realpath配合Path对象避免字符串拼接带来的意外。3. 越权漏洞实战拆解从水平/垂直到sa-token横纵越权3.1 水平越权、垂直越权、横纵越权到底怎么区分越权的基础概念很多人清楚但真到报告里看到“横纵越权”这个词还是会愣一下。我一般这样区分水平越权是同一个角色层级之间的越权普通用户A通过修改请求中的ID看到了普通用户B的订单垂直越权是不同角色层级之间的越权普通用户直接调用管理员的删除接口。水平越权考的是业务数据的归属校验垂直越权考的是角色权限的接口拦截。那横纵越权是什么它不是一个新漏洞类型而是中文社区里对“同时具备横向和纵向越权能力”的一种统称。比如攻击者既能通过修改参数访问任意用户的资源又能直接调用管理员接口执行敏感操作那这条越权链路就是横纵贯通的双向越权。近年来“sa-token横纵越权”这个词频繁出现在漏洞报告里其实就是Java Web项目用了sa-token框架做鉴权但由于接口权限注解配置不当导致横向与纵向越权同时存在的典型场景。3.2 sa-token横纵越权真实场景与代码层面的坑sa-token是目前Java生态里很流行的轻量级鉴权框架用法本身不复杂常规操作是Controller上加注解SaCheckLogin GetMapping(/order/detail) public Result getOrderDetail(Long orderId) { Order order orderService.getById(orderId); return Result.ok(order); }这里就是最常见的坑。SaCheckLogin只校验“用户是否登录”完全没有校验“这个订单是不是当前登录用户的订单”。攻击者登录自己的账号拿到Token后把请求里的orderId改成别人的订单号只要订单表里存在这个ID数据就被平白泄露了。这是典型的水平越权。再来看垂直越权SaCheckLogin PostMapping(/admin/deleteUser) public Result deleteUser(Long userId) { userService.removeById(userId); return Result.ok(); }接口路径带着admin但注解只拦了登录态普通用户登录后照样可以调用这个接口删人。这类问题在sa-token项目里特别容易出因为默认注解只加了SaCheckLogin而开发同学往往忘了补SaCheckRole(admin)或SaCheckPermission(user:delete)。横纵越权就是这么来的横向能遍历任意用户数据纵向能执行管理员操作攻击链路完整闭合。3.3 越权测试流程登录态、参数替换与角色切换我的越权测试流程稳定下来就四个步骤。第一步准备两个不同权限的账号。至少要有一个普通用户A和一个普通用户B如果可能再准备一个管理员账号C保证数据上可区分。第二步用A账号正常登录抓取请求包找到携带身份信息的参数——可能是userId、orderId、shopId也可能藏在请求体JSON字段里。第三步把A请求里的ID替换成B的ID重放请求如果响应里返回了B的数据水平越权确认。第四步用A的Token直接请求管理员接口如果返回200且操作成功垂直越权确认。判断标准很简单凡是影响范围跨越了当前登录用户自身的数据和权限都算越权。但要注意一个细节有些接口第一次替换ID后返回了空数据不一定代表安全可能是用户B不存在要换一个真实存在的ID再验证。另外很多系统存在“越权写入”场景比如修改用户资料的接口你把userId改成别人的ID成功的标志是响应正常而对方资料被改掉这种危害比读数据更大测试时同样要覆盖。3.4 修复思路统一鉴权与数据级权限校验修复越权我坚持两个原则第一业务数据归属必须由服务端从会话里取绝不信任前端传的ID第二接口权限校验必须统一收敛不能靠开发自觉。以sa-token为例获取当前登录用户的正确姿势是SaCheckLogin GetMapping(/order/detail) public Result getOrderDetail(Long orderId) { // 从会话中获取当前登录用户ID而非从前端参数获取 Long currentUserId StpUtil.getLoginIdAsLong(); Order order orderService.getById(orderId); if (order null || !order.getUserId().equals(currentUserId)) { return Result.fail(无权访问); } return Result.ok(order); }垂直越权的修复则要补上角色或权限码校验SaCheckRole(admin) PostMapping(/admin/deleteUser) public Result deleteUser(Long userId) { userService.removeById(userId); return Result.ok(); }如果项目接口很多我建议用全局拦截器或Spring AOP统一扫描注解而不是依赖开发在每个方法上手动加。除此之外数据级权限的另一种有效手段是在SQL层强制拼接当前用户条件比如MyBatis的拦截器自动给查询语句追加AND user_id ?这样即使业务层漏写了校验数据也取不出来。最后上线前用自动化规则扫描“从request.getParameter取值后直接查询数据库”这类模式能提前拦住大部分越权。4. 信息泄露实战拆解HeapDump、报错和扫描器告警4.1 信息泄露的高频泄露面远不止404报错信息泄露是三类漏洞里最“大而全”的一类它的泄露面广得超乎想象。最常见的几个口子第一个是详细报错页面没关数据库连接异常时直接把堆栈打到前端里面带着SQL语句和数据库地址第二个是备份文件残留服务器上躺着web.zip、db_backup.sql、config.php.bak攻击者用目录遍历或直接猜路径就能下载第三个是调试接口未收敛比如Swagger UI文档泄露所有接口定义配合越权直接变成攻击地图第四个是前端代码里硬编码密钥JS文件里的AccessKey、阿里云Secret、微信支付Key很多都是开发测试时顺手写的上线后忘了删。还有一个容易忽略的是日志文件。系统的访客日志如果把请求参数原样打出来那用户密码、Token、身份证号就全都进了日志文件。如果日志系统本身没做权限控制再叠加目录遍历等于把核心数据直接送给了攻击者。4.2 Spring Boot HeapDump敏感信息泄露一个口子就是一锅端最近两年Spring Boot Actuator的HeapDump泄露在攻防演练里出镜率非常高。原因很简单JVM堆内存里存着大量的运行时敏感数据而Spring Boot一旦开启了Actuator的heapdump端点且没有加鉴权攻击者直接请求GET /actuator/heapdump就能把当前进程的堆转储文件下载到本地大小通常几百MB到几个GB。拿到文件后用Eclipse MAT打开跑几条OQL就能把内存里的密码、Token翻出来。我实际用过的高命中查询语句select * from java.lang.String where toString().contains(password) select * from java.lang.String where toString().contains(secret) select * from java.lang.String where toString().contains(token)一条命令下去什么数据库密码、Redis密码、第三方API密钥、用户登录Token全部暴露无遗。我曾在一次授权测试里用这条链路十分钟内从heapdump里找到了生产数据库的明文密码比任何漏洞利用都直接。修复方案其实不复杂。在application.yml里只暴露必要端点management: endpoints: web: exposure: include: health,info如果业务确实需要保留某些端点必须设置独立端口加认证并且配合网络ACL限制访问来源。但这里有个坑很多团队以为配置了include: health,info就完事了实际上配置可能被后加载的配置中心覆盖所以改完必须实测一遍/actuator/heapdump是否真的返回404或401。另外一旦确认heapdump曾被下载过不要只删文件了事内存里所有敏感凭据都要轮换密钥泄露是无法通过“删文件”来挽回的。4.3 扫描器报CVE-2016-2183【原理扫描】到底要不要管很多运维收到安全扫描报告看到“SSL/TLS协议信息泄露漏洞(CVE-2016-2183)【原理扫描】”这几个字就慌了以为服务器已经被攻破。这里要解释清楚CVE-2016-2183对应的是SWEET32攻击针对的是SSL/TLS协议里3DES、Blowfish这类使用64位分组密码的加密套件。由于分组长度过短攻击者通过长时间采集流量理论上可以恢复HTTPS会话中的部分明文信息。“原理扫描”的意思是扫描器并没有真正发起SWEET32攻击而是通过TLS握手探测发现服务器支持的加密套件列表里包含了这类弱加密算法于是判定存在风险。说白了这是配置基线类问题不代表已经被利用但确实降低了加密强度。处理方式也比较直接在Nginx或负载均衡配置里禁用3DES、RC4等弱套件优先使用AES-GCM这套AEAD算法。Nginx里可以这样配置ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:HIGH:!aNULL:!MD5:!3DES; ssl_prefer_server_ciphers off;如果你的服务前面还有CDN或云负载均衡那还要去云平台控制台把SSL策略调成“TLS1.2加密套件AES-GCM”的等级。修复后要让扫描器重新探测确认因为有些四层负载均衡并不会透传客户端的加密套件协商扫描器可能扫到的是负载均衡自身而非后端这类问题在排查时要特别留意。4.4 信息泄露的真正价值它在攻击链里扮演的角色信息泄露经常被当成“低危”漏洞但从实战角度看它往往是整条攻击链的起点。没有信息泄露攻击者就像蒙着眼睛进门有了信息泄露目录遍历有了目标知道该读哪个文件越权有了凭据知道用什么Token、扮成哪个用户可以说它是让攻击从“碰运气”变成“按图索骥”的关键转折点。这也是我为什么强烈建议不要把信息泄露类漏洞单独降级处理。在一次完整风险评估里我会把“泄露了什么”“泄露的口子能不能被其他漏洞复用”“泄露的数据能否直接支撑下一步利用”这三点写清楚。一个能帮助攻击者拿到数据库密码的heapdump漏洞在我这里的优先级比一个无法利用的“低危指纹泄露”要高得多。5. 三漏洞横向对比与一套完整防御设计5.1 三类漏洞的特征对比表把这三类漏洞放在一张表里对比能更清楚地看到它们在性质、入口、影响和修复重点上的差异对比维度目录遍历越权信息泄露本质路径/文件系统边界失效权限/授权模型边界失效数据暴露面失控常见入口文件下载、预览、导入导出业务接口中的ID类参数报错页、备份文件、调试端点主要影响读取服务器任意文件横向访问他人数据、纵向提权凭据、配置、敏感数据外泄黑盒验证路径参数注入../替换请求中归属ID、调用高权限接口遍历常见路径检查响应内容修复重点白名单、路径归一化、安全API统一鉴权、数据级权限校验收敛暴露面、密钥轮换、关闭调试开关这张表最大的价值不是罗列差异而是帮助团队在做安全设计时对齐一个问题我们到底要把边界设在哪儿目录遍历的边界在文件系统入口越权的边界在授权决策点信息泄露的边界在生产环境所有对外输出的出口。三条边界各守一摊任何一条失守都可能成为整条链路的突破口。5.2 一个组合攻击链的复盘目录遍历→信息泄露→越权说一个我实际参与复盘过的案例。某内部管理系统上线前扫描报告里同时出现了目录遍历、敏感信息泄露、越权三类问题。当时开发觉得都是中低危不着急修直到攻防演练里被完整走通才意识到问题的严重性。攻击者先通过一个图片预览接口做了目录遍历读到了application.yml配置文件拿到了数据库地址、账号前缀和Redis连接密码。这一步打开了一个大口子因为配置文件里明文写了数据库账号密码虽然部分打码但紧接着攻击者发现系统的/actuator/heapdump端点没有关下载堆转储后用MAT搜索直接把完整的数据库明文密码翻了出来。之后攻击者用数据库账号连接了内网数据库拿到一批真实用户的身份信息和登录Token。最后利用一个订单查询接口的水平越权替换orderId参数成功访问了任意用户的订单数据包括收货地址、手机号等敏感信息。复盘时我们得出结论如果当初任何一个环节的边界守住了这条链都走不通。目录遍历被拦住配置文件就不会泄露heapdump关掉密码就拿不到接口做数据归属校验即使有Token也横向拖不走数据。所以防御不是修一个漏洞而是把整条链上的所有边界都修一遍。5.3 我在实际项目中用的防御工作流经过这些年的实践我沉淀了一套针对这三类漏洞的防御工作流分为三个环节。开发阶段把编码规范做成自动化检查项。禁止字符串直接拼接文件路径禁止从前端参数取值后直接做数据库查询禁止在Controller里出现request.getParameter(userId)这类代码。这些规则可以写成IDE插件提示或者接入代码扫描平台如SonarQube的策略集让问题在代码评审之前就被机械地拦下来。测试阶段安全测试不能只看单漏洞。我会要求测试同学把扫描器结果按攻击链重新组织先看信息泄露暴露了什么再看目录遍历能不能把这些信息变成实际文件最后看越权能不能把这些信息变成业务数据。每一条能走通的路就是一次完整的风险闭环。运维阶段重点收敛暴露面并做好密钥管理。Actuator、Swagger、备份文件、调试开关这类敏感内容只允许在测试环境开生产环境一律关闭生产环境所有数据库和中间件的密码不允许明文写在配置文件里统一用密钥管理服务密钥必须定期轮换尤其是发生过泄露窗口的情况下这个动作不能省。6. 常见问题速查与排障心得6.1 高频问题速查表问题现象可能原因排查方向目录遍历修复后仍可绕过过滤逻辑只做了一次替换/解码统一使用路径归一化和startsWith校验接口加了SaCheckLogin仍出现越权注解只验证登录态未验证数据归属和角色确认业务数据归属是否从Session获取是否补了角色/权限注解/actuator/heapdump明明配置了还是能访问配置中心或启动参数覆盖了本地yml启动后实测端点返回码检查配置加载顺序扫描器反复报CVE-2016-2183服务端仍支持3DES/RC4弱套件检查Nginx、负载均衡本身的SSL策略改完重新探测信息泄露问题“时有时无”调试模式开关、灰度配置不一致统一配置中心管理环境开关生产关闭所有调试输出这张表里的每一条都是我或团队在实际项目中遇到过的问题。尤其注意第三行我见过太多“明明改了配置测试时才发现根本没生效”的情况所以每次修改完配置后的实测回放比配置本身更重要。6.2 几个写在最后的实操心得第一个心得整理漏洞清单时不要按“高中低危”排序而是按攻击链的依赖关系排序。先解决能支撑后续利用的信息泄露和目录遍历再解决越权这样即使某一条漏掉也不至于让攻击者拿到完整链路。第二个心得修复越权时“永远不要信任前端传过来的ID”这句话值得贴在工位上。无论是userId还是orderId都只把前端传值当检索条件权属判断必须由服务端从会话、Token或服务端存储中确认。第三个心得信息泄露类漏洞修完后记得追加密钥轮换动作。很多团队删掉了heapdump端点、关闭了详细报错就认为事情结束了但已经暴露出去的密码和Token不会自动失效。不轮换等于白修。第四个心得合规扫描里的“原理扫描”类告警不要无视也不要恐慌。它代表配置层面的风险未必被实际利用但既然能被扫出来就一定存在被利用的可能。花半小时把弱加密套件禁掉比反复争论“这个漏洞到底有没有用”要划算得多。另外复测的时候我习惯换几种编码再打一遍同样的Payload。很多修漏洞的人只测试了原始字符串形式的../没测过URL编码、双重编码、正斜杠和反斜杠互换这些变体。目录遍历这类漏洞过滤器稍微写得不严谨就会被编码绕过。多测一轮变体比事后被攻击者抽冷子打穿要安心得多。