目录遍历、越权与信息泄露:原理、排查与修复实战指南
2026/9/15 18:56:23 网站建设 项目流程

目录遍历、越权、信息泄露,这三类漏洞放在一起讲,其实挺有讲究的。我在做安全测试和应急响应的这些年里,遇到过太多因为这三类问题翻车的项目,小到个人博客被挂马,大到核心业务数据库被拖走,源头往往就是其中某一个看起来"技术含量不高"的隐患。不过有意思的是,它们虽然成因不同、表现各异,却经常在同一个系统里同时出现,而且都指向同一件事:对用户输入和访问边界的信任过了头。

这篇文章我打算按目录遍历、越权、信息泄露的顺序,逐个拆开讲清楚它们的原理、高发场景、排查思路和修复方法,也会结合像 sa-token 横纵越权、Spring Boot heapdump 敏感信息泄露、SSL/TLS 弱加密算法这类近期比较热的问题,聊聊我在实际项目中踩过的坑和验证过的做法。内容主要面向做开发、测试、运维以及刚转安全的朋友,老手也可以重点看看越权自查方案和漏洞优先级排序那部分。

1. 目录遍历漏洞:老掉牙却反复出现的高危问题

1.1 目录遍历的原理到底是什么

目录遍历漏洞,英文一般叫 Directory Traversal 或 Path Traversal,本质上是攻击者通过输入特殊构造的路径序列,让服务器跳出原本限定的目录范围,去读取或操作其他文件。最常见的输入就是../这种相对路径跳转符,配合文件名,就能拼出像../../../../etc/passwd这样的路径。

服务器端接收参数后,如果直接把这个值拼接到文件路径里,用file_get_contentsFiles.readAllBytessendFile这类函数去读取文件,或者用Runtime.execProcessBuilder去执行命令,就会出问题。你传入一个../,操作系统就往上跳一级,连续跳几次就跳出网站根目录了。

听起来很简单,但它变种很多。比如在 URL 里直接传..%2f(即../的 URL 编码形式),或者用双重编码..%252f,还有用绝对路径/etc/passwd直接读取、用....//绕过简单过滤等等。我在测试中见过不少开发者对../做了字符串替换,但如果只把../替换成空字符串,攻击者构造....//时,替换掉中间的../后反而留下../,照样能穿目录。这类过滤绕过问题,是目录遍历漏洞排查中绕不开的一环。

1.2 为什么它在这么多年后依然高频出现

你可能会想,这种漏洞十年前就有了,怎么现在还这么普遍。实际排查下来的原因主要有三个。

第一个是文件下载、文件预览、导出报表这类功能天生就容易踩坑。只要有"根据用户传入文件名去服务器上取文件"这种逻辑,就存在目录遍历的风险。比如一个导出 Excel 的功能,前端传fileName=xxx.xlsx,后端直接拼接路径去读文件,攻击者把参数改成../../config/db.properties,敏感配置就出去了。我扫过的很多系统,这类问题往往集中在文件下载接口、图片预览接口、模板加载接口上。

第二个是过滤逻辑不严谨。很多团队的防御方式是"过滤掉..",而不是"校验最终路径是否在允许范围内"。前者只要存在编码变体、嵌套绕过等方式就很容易失守,后者才是相对稳妥的思路。比如先对路径做规范化(canonicalization),再检查规范化后结果是否以允许目录前缀开头,这才是有效做法。

第三个是测试覆盖不足。开发自测时一般只验证正常路径,比如传个合法文件名能正常下载,很少有人主动传../../去试异常场景。这个问题在接口文档里通常也写不清楚"哪些参数是文件路径、哪些目录是允许访问的",等安全测试提出来,往往功能已经上线了。

1.3 目录遍历的检测方法和修复方案

先说检测。在没有专业扫描器的情况下,我常用的方法是先用接口文档或者抓包,把涉及文件操作的接口全部找出来,重点看参数名里带 fileName、filePath、path、url、template 的。然后直接修改参数值,在正常文件名的前面或中间插入../序列,观察响应内容变化。

一个很有效的测试思路是:先请求一个不存在的文件名,比如abc_does_not_exist.txt,看报错信息是否暴露了物理路径;再请求../../../../etc/passwd,在 Linux 环境下一旦响应里出现root:x:0:0:之类的行,基本就确认了。Windows 环境可以试..\..\..\..\windows\win.ini。需要说明一点,所有测试都必须在你自己授权范围内进行,别拿这套方法去碰不属于你的系统,这是行业底线。

修复方面,我给开发团队推荐过一套相对完整的方案,按优先级排序:

  • 首选方案是白名单校验。如果业务允许访问的文件是有限的,直接用白名单映射,前端传文件 ID 而不是路径,后端在 Map 里找对应文件名,这种方法最安全,因为攻击者根本没有机会传路径进来。
  • 如果必须支持动态文件名,那就要对用户输入做严格校验。拒绝所有包含../\、空字节的输入,同时做 URL 解码后再校验,避免编码绕过。
  • 在 Java 里,可以用Paths.get(baseDir, userInput).normalize()得到规范化路径,再用startsWith(baseDir)判断是否越界;在 Python 里类似,用os.path.realpath()取真实路径后判断。这套逻辑已经能堵住大部分问题。
  • 底线上,要把应用进程权限降下来,让 Web 服务只能访问网站目录和必要的读写目录,即使被穿越,也拿不到系统关键文件。

2. 越权漏洞:水平越权与垂直越权全面拆解

2.1 水平越权和垂直越权到底怎么区分

越权漏洞,也叫访问控制漏洞,是目前业务逻辑漏洞里最头疼的一类。它分两种:水平越权和垂直越权。很多同行管它们叫横向越权和纵向越权,意思一样,只是叫法不同。

水平越权,简单说就是"同级用户之间越权访问"。两个用户都是普通用户,A 用户通过修改请求中的用户 ID、订单号、发票号这类资源标识,就能读取、修改 B 用户的私有数据。比如一个查看订单详情的接口,URL 是/order/detail?orderId=10001,后端不加校验直接按这个 ID 查询并返回,那我把 10001 改成 10002,就能看到别人的订单了。典型场景包括:查看他人订单、修改他人个人信息、操作他人购物车、查看他人简历、查看他人私聊记录等。

垂直越权则是"低权限用户访问高权限功能"。普通用户直接请求管理员接口,或者普通员工调用 HR 系统里只有 HR 才能用的接口,都属于垂直越权。它的判断标准不是"能不能登录",而是"登录后能不能调用超出自己角色权限的接口"。常见的表现有:前端把按钮隐藏了,但后端没做权限校验,攻击者直接模拟 HTTP 请求就能访问管理员功能。

这两个概念经常被放在一起讨论,是因为它们常常同时出现在一个系统里:某些接口既没校验"资源是否属于当前用户"(水平),也没校验"当前角色是否有权访问"(垂直),那就是双重越权。这也是为什么最近"横纵越权"这个词会火起来,很多安全公告里直接用"横纵越权"来描述一个系统同时存在水平越权和垂直越权的情况。

2.2 越权漏洞为什么是"逻辑漏洞之王"

越权漏洞之所以危险,不是因为技术难度高,而是因为它极其隐蔽且杀伤力极大。SQL 注入、RCE 这类漏洞靠扫描器就能发现一大堆,但越权漏洞必须理解业务逻辑才能识别。很多自动化扫描器根本不知道"这个订单 ID 应该是属于当前用户的",它只看响应状态码和内容,所以漏报率很高。

我从经验来看,越权漏洞的高发位置集中在以下几类接口:

  • 根据 ID 查询详情类的接口。比如订单详情、用户详情、消息详情,这类接口天生以 ID 为参数,最容易出现水平越权。开发人员往往默认"别人不可能猜到 ID",但 ID 通常是自增数字,猜到只是时间问题。
  • 状态变更类的接口。比如订单改价、用户禁用、退款审批、文章删除,这类接口一旦缺少角色校验,就会出现普通用户修改管理数据的垂直越权。
  • 批量导出和列表查询。很多列表接口支持传 userId、deptId 来筛选数据,如果当前用户的身份不是从 Session/Token 里取,而是从请求参数里取,那直接把参数改成别人的 ID 就能导出别人的数据。
  • 文件上传和下载。上传接口如果不校验文件归属,可能 A 用户上传的文件可以被 B 用户覆盖或删除。

越权漏洞修复的难点还在于:它的根因是"缺少统一的鉴权模型"。业务接口多、权限规则复杂,每个接口单独判断就容易漏。很多项目前期没做权限设计,后补的话改动量巨大,这也是为什么很多系统存在大量越权问题却迟迟不修。

2.3 结合 sa-token 谈横纵越权的成因与防护

说到 Java 后端的越权防护,sa-token 是现在比较流行的鉴权框架,在前后端分离的项目里用得很多。它有登录认证、权限认证、SSO、OAuth2 等功能,本身设计是没什么问题的,但我在实际排查中见过不少因为"用法不对"导致的越权。

最常见的一种错误是:只配置了登录认证拦截器,确保"没登录不能访问",但没做权限认证,意味着只要是登录用户,不管什么角色都能访问所有接口。比如一个后台管理接口,只加了StpUtil.checkLogin()来验证是否登录,但没校验当前用户是不是管理员,普通用户登录后直接请求这个接口一样能通。这就是典型的垂直越权,而且是横纵越权里最常见的一种成因。

另一种错误是:获取当前用户 ID 的方式不对。有些代码里写Long userId = Long.parseLong(request.getParameter("userId")),从请求参数拿用户 ID,然后拿这个 ID 去查询用户数据。这样就完全绕过了权限体系,攻击者想查谁就查谁,水平越权就来了。正确做法应该是从当前登录状态里取,sa-token 里通常用StpUtil.getLoginId()拿到当前会话对应的用户 ID,然后校验这个用户是否有权限访问目标资源。

还有一种容易忽略的场景:sa-token 的权限认证依赖权限码或角色码,需要在登录时给用户赋予对应的权限。如果权限码本身设计得粗糙,比如所有普通用户都拥有一个写死的"admin:all"权限,那权限校验形同虚设。务实点讲,做权限设计时应该遵循最小权限原则,按角色拆分权限码,并且要区分"数据级权限"和"功能级权限"。sa-token 能做功能级权限校验,但数据级权限(比如"只能看本部门的订单")需要业务层自行判断,很多越权漏洞恰恰发生在数据级权限缺失上。

2.4 一套可落地的越权自查方案

先说结论:越权漏洞的测试基础是"接口级黑盒 + 业务理解",纯跑扫描器基本没用。我自己的自查流程大致如下。

第一步,梳理所有涉及"资源 ID"的接口。把接口文档导出来,逐个标出哪些参数是资源标识(如 ID、编号、单号),这个清单就是测试范围。没有接口文档的项目,可以通过抓包把所有 API 过一遍,重点找 restful 风格的接口。

第二步,准备两个测试账号。水平越权测试必须要有两个同级账号,比如 userA 和 userB。用 userA 登录,拿到自己某个资源的 ID,再换 userB 的 Token 去请求 userA 的资源 ID,如果成功返回数据,就说明存在水平越权。这里有个实用技巧:不要只测一个接口,要批量遍历 ID 范围,很多系统"单个越权"和"可遍历多个 ID"的危害等级完全不同。

第三步,测试垂直越权。准备一个普通用户账号和一个管理员账号,先拿管理员请求一个管理接口的完整请求包,把 Token 换成普通用户的 Token,再发送一次,看是否仍有权限。如果返回 200 且包含管理数据,就是垂直越权。

修复层面我给开发同学的建议是:所有涉及用户私有数据的接口,都必须做"资源归属校验",判断当前登录用户 ID 是否等于资源所属用户 ID;所有管理功能接口,必须做"角色/权限校验",仅校验登录状态远远不够。如果有条件,把两类校验抽象成公共注解或统一拦截器,避免每个接口各写一套。另外,接口返回数据时也要注意不要泄露不该返回的字段,比如用户表里如果存了手机号、身份证号,列表接口默认就别返回,要用的时候单独查。

3. 信息泄露漏洞:敏感数据是怎么悄悄流出去的

3.1 信息泄露的常见类型

信息泄露漏洞是个"大筐",只要是敏感数据暴露给了不该看到的人,都能算这一类。从我在实际项目里遇到的案例来看,比较高频的有下面几种。

第一种是目录/文件泄露。比如网站根目录下存在.git目录,通过工具就能把源码历史记录拉下来;又比如允许目录浏览,访问某个路径直接列出所有文件名。我曾经在一个客户的项目里发现/backup/目录可以直接浏览,里面躺着一份三个月前的数据库备份,下载下来解压就能看到明文密码。这种情况在开发环境变成生产环境时特别常见,备份脚本只改了路径,没改访问权限。

第二种是错误信息和版本信息泄露。页面报错时直接输出堆栈、SQL 语句、物理路径,攻击者根据报错信息能判断出用了什么框架、什么数据库、什么版本,针对性找漏洞就容易多了。比如日志里打出org.springframework.jdbc的包名,基本能确认是 Spring Boot + JDBC;再比如某些框架的默认报错页会直接显示框架版本,拿到版本号就能去查这个版本有哪些已知漏洞。

第三种是接口/配置信息泄露。Swagger/OpenAPI 文档暴露在公网,把系统所有接口定义、参数模型全都看光了;Actuator 监控端点暴露,/actuator/env/actuator/health把环境变量、配置项漏了出来;还有前端打包时候注释没清,源码里带着内网 IP、数据库地址、第三方平台密钥。这类问题防不胜防,我在很多前端 JS 文件里都挖到过硬编码的密钥。

第四种是第三方服务的信息泄露。比如把阿里云 AccessKey 写死在代码或配置里,然后被拖进公开仓库;再比如对象存储桶权限配置错误,变成公共读,任何人都能列出并下载桶内所有文件。这类泄露通常影响范围极大,因为云厂商的 AccessKey 往往关联着大量资源和费用。

3.2 Spring Boot heapdump 敏感信息泄露

近期"Spring Boot heapdump 敏感信息泄露"这个话题热度不低,我也借这个机会展开说说。heapdump 是 Java 虚拟机把当前堆内存快照导出的文件,里面包含了进程内存中的所有对象。正常来说,这是故障定位时用来分析内存溢出的工具,但如果暴露到公网,问题就大了。

为什么说 heapdump 泄露很致命?因为应用运行期间,内存里存着大量敏感数据:当前登录用户的会话 Token、数据库连接串(可能带明文密码)、第三方服务密钥、还在内存里未被序列化的业务数据。我在一次应急响应中,客户怀疑系统被人提权了,排查后才发现对方先下载了/actuator/heapdump文件,用工具解析出内存里的数据库账号密码,然后直连数据库拖库。整个过程不需要任何高深技巧,因为 Spring Boot 的 heapdump 是个固定路径,只要端口暴露,任何人都能下载。

排查和修复思路也很直接。第一,检查 Spring Boot 配置,把 Actuator 的端点限制在内网或干脆关闭。生产环境至少要把/actuator/heapdump/actuator/env/actuator/configprops/actuator/mappings这类高危端点禁掉。第二,如果业务确实需要 Actuator 做监控,务必通过独立的监控端口和管理网络访问,不要和业务端口混在一起。第三,定期用扫描器检查对外暴露的端点,很多安全团队开漏洞扫描时默认会探这些路径。另外关于 heapdump 的确认,常见工具性办法是用 JVM 的jhat或商业工具去解析,但普通开发人员不用深入,关键是确认生产环境根本没有这个文件可下载。

3.3 SSL/TLS 信息泄露漏洞

SSL/TLS 相关的信息泄露漏洞,近期被反复提及的是 CVE-2016-2183,也就是 SWEET32 攻击所涉及的弱加密算法问题。这个编号听起来很陌生,但原理其实不复杂:如果服务器配置了 3DES(三重数据加密标准)或 DES 这类分组加密算法作为 SSL/TLS 的加密套件,加密强度相对不足,在特定条件下可能被破解会话数据。很多漏洞扫描器报告"SSL/TLS 信息泄露漏洞 (CVE-2016-2183)【原理扫描】",就是检测到服务器支持的加密套件里包含这类弱算法。

这类问题在老旧服务器上特别常见。原因也很现实:服务器操作系统和 OpenSSL/Nginx/Apache 版本陈旧,默认配置里还带着 3DES。我处理过一个案例,一台跑着重要内部系统的服务器,扫描报告里报了 CVE-2016-2183,但信息部门担心禁用 3DES 会影响老旧浏览器访问,迟迟不敢动。后来查了一下连接日志,发现实际客户端都是现代浏览器,根本不依赖 3DES,最后把旧版本升级、弱加密套件禁用,扫描就干净了。

修复方法是分层的:在 TLS 配置层面禁用弱加密套件,比如 Nginx 里修改ssl_ciphers配置,去掉包含3DESDES的套件;更根本的是升级 OpenSSL 库和服务器系统,让底层不再支持过时的协议和算法。另外,我一直建议大家把 SSL/TLS 的安全基线检查纳入例行运维,每季度扫一次,而不是等到漏洞报告出来了才处理。

3.4 信息泄露漏洞的排查与修复思路

信息泄露和前面两类漏洞不同,它很少是"单个接口的问题",更多是"配置和管理的问题"。所以排查时我通常不是盯着接口一个个测,而是按资产维度来做。

我建议的顺序是:先盘点对外暴露的资产,把所有公网域名、IP、端口列出来,不看不知道,很多客户盘完才发现自己有十几个早就忘了的测试环境还挂在公网上。然后从四个方面自查:一是目录浏览和备份文件,用路径字典配合扫描器扫常见目录,比如/backup/git/web.config/application.properties;二是错误的报错信息,直接访问几个不存在的用户 ID,看报错内容是否会返回堆栈和 SQL;三是开发框架的默认端点,像 Spring Boot 的 actuator、Django 的 admin、phpMyAdmin 之类;四是前端源码,把 JS、CSS 文件下载下来搜里头的 key、token、password、api.xxx.com 这些关键字。

修复层面,核心原则是"最小暴露"。敏感接口能放内网就放内网,必须公网访问的要做访问控制和身份认证。文件上传和备份目录禁止脚本执行权限,web 根目录禁止目录浏览。框架的调试模式、默认控制台、监控端点在生产环境一律关闭。还有一个很实用的小技巧:把应用运行账号改成普通权限用户,即使被拿到 shell,也做不了太多破坏。

4. 常见问题与排查技巧实录

4.1 三类漏洞的优先修复顺序怎么定

在实际工作中,团队经常问我的问题是:这三类漏洞同时存在,先修哪个?我的排序思路是:垂直越权 > 目录遍历 > 水平越权 > 信息泄露,但具体要看数据敏感度和暴露情况。

垂直越权排第一,是因为普通用户直接打管理员接口,影响的往往是全量数据,一旦被利用整库都可能丢,危害面最大。目录遍历排第二,是因为它通常直接读服务器文件,容易拿到配置和密码。水平越权排第三,是因为它一般只能影响单个用户或小范围数据,虽然也很严重,但对比前面两个,影响面相对可控。信息泄露排最后不是说不重要,而是它更多是"攻击前的侦察帮助",真正被单独利用造成严重破坏的情况相对少,但它会放大前两类漏洞的破坏力。比如 heapdump 泄露了数据库密码,再配合目录遍历去找配置文件,上下攻击链就起来了,所以暂时排名靠后,但一定不能不管。

还有一个务实的建议:修复前先把所有公网入口先封一遍。比如把 actuator 端点全部禁用、把备份目录加访问认证,这些改动小、见效快,能在开发团队排期修复大功能之前,先把最明显的风险面收掉。

4.2 误报确认:如何判断一个告警是不是真漏洞

安全扫描器经常会报一堆误报,尤其像 CVE-2016-2183 这种"原理扫描"类问题,报出来不代表一定能被利用,但是也别直接忽略。我自己判断误报的套路大致是这样。

如果是目录遍历类告警,扫描器说某个参数存在路径遍历,我会手动验证一下:构造一个无害的测试路径,比如请求一个../../../../tmp/test_probe_2024.txt这种不存在的文件,如果返回 404,说明服务器确实按这个路径去找文件了,但没有读到内容,这时再尝试读取存在的文件确认。如果返回结果把路径原样返回但并不去读文件,很可能是扫描器根据响应内容误判。

如果是越权类告警,扫描器基本不会报,因为自动扫描器很难理解业务。所以更多情况是开发不承认:接口测出来能拿到别人的数据,开发说"这是正常的,用户本来就能查看文章"。这时关键是要看数据是否"私有"。一条订单、一封私信、一份简历属于资源所有者个人,那这类访问就是越权;一篇文章、一条公告是公共资源,那看到就正常。判断标准就是这么朴素。

如果是信息泄露类告警,比如 Actuator 端点,直接访问确认即可。参数值、响应内容、敏感字段都对应得上,基本就是实锤。唯一要注意的是有些系统在同一域名下挂了多个应用,扫描器报的路径可能在某个静态资源目录里压根不存在,这种直接去 URL 访问一下就能排掉。

4.3 我的几点实操心得

说了这么多,最后分享几个从项目里总结出来的实操体会,希望对大家有实际参考价值。

第一,越权漏洞的测试必须"懂业务"。我曾经花了一下午时间测一个 CRM 系统的导出接口,参数是customerId,用两个账号测了半天都显示无权访问,后来仔细看接口文档才发现,系统本身的业务规则就是"销售只能导出自己名下的客户",所以返回无权访问反而是正常现象。真正的越权点是"市场部角色可以导出全公司客户",这必须有业务常识才能判断。所以做安全测试不要只盯着技术,一定要先了解业务流程。

第二,修复目录遍历时,永远不要只做输入过滤。输入过滤是必要的,但只过滤../很容易被绕过。我见过最稳的做法是:在代码里对路径做两次解析,先 URL 解码,再做路径规范化,然后用startsWith检查最终路径是否在允许目录内。这套逻辑写成公共函数,所有文件操作统一调用,比每个接口各写一遍判断要靠谱得多。

第三,如果是团队协作,建议把这三类漏洞的测试用例沉淀成一份 checklist。目录遍历测什么参数、越权测哪些接口、信息泄露看哪些端点,写成文档放进 CI/CD 流程里,每次发版前自动跑一遍。安全不只是安全团队的事,开发自测阶段能拦掉 80% 的低级问题,后面整个团队都会轻松很多。我自己带过的项目里,凡是坚持做这一件事的,上线后漏报率明显低于其他项目。

我在实际工作中还有一个体会:这三类漏洞的修复方案本身都不复杂,难的是"发现"和"坚持"。很多团队的问题不是不会修,而是根本没有定期测、上线不检查,等到出事才匆匆忙忙处理。如果能把安全检测从"应急动作"变成"例行动作",很多风险根本走不到生产环境。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询