1. 项目概述:从SRC实战看API安全
最近在几个企业SRC(安全应急响应中心)平台上提交了几个中高危漏洞,类型出奇地一致:都是围绕API接口的未授权访问和越权问题。这让我意识到,虽然“未授权”和“越权”是老生常谈,但在API驱动的现代应用架构下,它们正以新的、更隐蔽的形式出现,并且危害巨大。很多开发团队在前后端分离、微服务化的过程中,把注意力都放在了功能实现和性能上,却忽略了API接口最基本的安全边界。今天,我就结合最近的实战案例,把这套“组合拳”的挖掘思路、测试手法和背后的逻辑掰开揉碎讲清楚,希望能给正在入门SRC挖掘的朋友,或是负责API安全的开发同学一些实实在在的参考。
简单来说,API接口未授权漏洞,指的是本应需要认证(比如登录)才能访问的接口,在未提供任何有效凭证的情况下,直接被访问并操作成功。而越权漏洞,则是在拥有一个合法低权限账户的前提下,能够访问或操作本应属于其他用户(水平越权)或更高权限角色(垂直越权)的数据或功能。这两者常常相伴相生,一个未授权的接口可能直接导致越权,而一个设计不当的授权校验逻辑,则会让越权在授权之后依然发生。对于SRC猎人而言,这类漏洞逻辑清晰、危害直接、复现简单,是性价比极高的目标。
2. 漏洞原理与核心攻击面拆解
要挖洞,先得懂原理。我们不能停留在“抓个包改个ID”的层面,必须理解这些漏洞为何会产生,以及它们通常隐藏在哪些地方。
2.1 未授权访问:缺失的“门卫”
你可以把API接口想象成公司大楼里的各个房间。未授权访问,就相当于某个重要房间(比如财务室)的门锁坏了,或者压根没装锁,任何人都能直接推门进去。
从技术实现上看,导致未授权的原因主要有以下几类:
- 开发疏忽或配置错误:这是最常见的原因。开发人员可能为了调试方便,临时注释掉了接口的鉴权拦截器(如Spring Security的
@PreAuthorize注解、拦截器Interceptor),上线时却忘了恢复。或者,在配置路由规则时,错误地将本应受保护的管理员API路径排除在了安全框架的过滤规则之外。 - 默认配置与弱口令:许多中间件、运维工具或框架的管理API在安装后存在默认端口、默认路径和默认空口令/弱口令。例如,我之前遇到过一个案例,目标的
Nacos服务发现与配置中心,其管理界面虽然需要登录,但用于服务发现的HTTP API接口(/nacos/v1/ns/instance/list)在某个旧版本下存在未授权访问漏洞,可以直接拉取到所有注册服务的敏感配置信息。再比如,某些Swagger、Actuator端点未做访问控制,直接暴露了完整的API文档甚至执行端点。 - 接口路径猜测与目录遍历:系统可能存在一些未被前端引用的“隐藏”接口,或者遵循某种规律的接口路径。攻击者通过字典爆破或分析现有接口的路径规律(如
/api/user/1、/api/admin/config),可能直接访问到未在安全管控范围内的功能接口。 - 认证逻辑缺陷:接口声称需要认证,但认证校验逻辑存在严重缺陷。例如,仅检查请求中是否存在
token字段,而不验证其有效性;或者认证逻辑依赖于前端传递的某个易篡改的参数(如isAdmin=true)。
注意:未授权访问的危害是立竿见影的。它可能直接导致数据泄露(下载数据库备份、读取配置文件)、系统控制(执行命令、上传木马)或作为进一步攻击的跳板。
2.2 越权漏洞:失效的“权限检查”
越权漏洞发生在“门卫”检查了你的工牌(认证通过),但没有核对你的权限清单。你拿着A部门的门禁卡,却刷开了B部门的核心机房。
其核心在于服务端在处理请求时,未能对当前请求发起者的身份与其所要访问的资源/操作进行充分的关联校验。主要分为两类:
- 水平越权(同权越权):用户A可以操作用户B的数据。最常见的就是通过修改请求参数中的ID(用户ID、订单ID、文章ID)来访问他人信息。例如,访问
/api/order/1001可以看到自己的订单,将ID改为1002后,竟然成功看到了别人的订单详情和收货地址。 - 垂直越权(提权):低权限用户能够执行高权限用户的操作。例如,普通用户通过访问
/api/admin/deleteUser接口,成功删除了其他用户。这通常是因为服务端仅验证了用户是否登录,但没有校验用户角色(ROLE)或权限点(Permission)。
越权的根本原因在于:
- 服务端完全信任客户端传参:直接使用客户端提交的用户ID进行查询,没有从会话(Session)或令牌(JWT)中提取当前登录用户的真实身份进行比对。
- 权限校验粒度粗放:只在进入控制器(Controller)时做了角色校验(如
@PreAuthorize(“hasRole(‘ADMIN’)”)),但在具体的业务方法中,没有对“当前用户是否有权操作这个具体的数据实体”做二次校验。 - 前端鉴权,后端不鉴权:这是致命错误。所有权限校验必须放在服务端执行,前端隐藏按钮、禁用菜单仅仅是用户体验优化,绝非安全措施。
2.3 API安全测试的独特挑战
与传统Web应用不同,API(特别是RESTful API)的安全测试有一些特殊点:
- 无状态性:很多API使用Token(如JWT)而非Session,攻击者一旦获取Token,即可在有效期内模拟用户所有操作。
- 数据驱动:输入输出多为结构化数据(JSON/XML),漏洞可能隐藏在复杂的JSON对象嵌套中。
- 自动化程度高:API易于被脚本批量调用,这意味着一个漏洞可能被瞬间利用成千上万次。
- 文档与调试工具暴露:
Swagger-UI、knife4j等API文档工具若未授权访问,相当于给攻击者提供了完整的“攻击地图”。
3. 实战挖掘流程与工具链
理论懂了,我们上实战。下面是我个人总结的一套针对API未授权/越权漏洞的标准化挖掘流程。
3.1 信息收集:绘制API地图
信息收集的广度决定了你攻击面的宽度。
主动爬取与目录扫描:
- 工具:
Burp Suite(爬虫)、dirsearch、ffuf、gobuster。 - 方法:使用Burp对目标应用进行全站爬取,重点关注
/api/、/v1/、/rest/、/graphql等常见API路径。同时,使用目录扫描工具,搭配强大的API路径字典(如api-words.txt、spring-boot.txt)进行爆破,寻找隐藏接口。 - 技巧:分析已发现的API路径模式。如果发现
/api/users/,尝试/api/admins/、/api/config/。留意数字ID,尝试遍历。
- 工具:
挖掘API文档与调试端点:
- 常见路径:
/swagger-ui.html,/v2/api-docs,/swagger//doc.html(knife4j)/actuator,/actuator/health,/actuator/env(Spring Boot Actuator)/api-docs,/openapi.json
- 动作:直接浏览器访问这些路径。一旦发现未授权的API文档,你就获得了所有接口的详细说明、参数和请求格式,事半功倍。
- 常见路径:
分析前端代码(JS文件):
- 方法:在浏览器开发者工具的
Sources或Network面板中,仔细查看加载的JavaScript文件。现代前端框架(Vue、React)通常会将API请求地址硬编码或通过配置引入JS中。搜索关键词如axios.get、fetch、/api/、http://,可以找到前端使用的所有API端点。
- 方法:在浏览器开发者工具的
识别中间件与管理服务:
- 目标:通过端口扫描(
nmap)或网络空间搜索引擎(如fofa、shodan)识别目标开放的非Web端口。 - 高风险服务:
Redis(6379): 未授权访问可导致数据泄露甚至服务器沦陷。MongoDB(27017): 默认无认证。Elasticsearch(9200): 未授权访问可查询所有索引数据。Jenkins(8080): 控制台可能未设置密码。Kubernetes API Server(6443, 8080): 配置不当可导致集群接管。H2 Database Console(通常嵌入在Spring Boot应用中): 未授权访问可直接执行SQL。
- 目标:通过端口扫描(
3.2 漏洞探测:从改包到逻辑分析
拿到API列表后,进入核心测试环节。
未授权访问测试:
- 步骤:对于收集到的每一个API端点,在未登录状态下(或使用一个无效/空的Token),直接重放请求。
- Burp技巧:使用
Repeater模块。先拦截一个已登录的请求,然后删除请求头中的Authorization、Cookie、X-Token等认证字段,或者将其值改为一个明显错误的字符串,再发送请求。 - 判断依据:如果返回了非401(未认证)或403(禁止访问)的状态码,并且返回了有效数据或操作成功的提示,那么极有可能存在未授权访问漏洞。需要特别注意200状态码下的“成功”信息,以及302重定向(可能重定向到登录页,也可能重定向到内部管理页)。
水平越权测试:
- 前提:你需要至少两个同一权限级别的测试账号(如UserA和UserB)。
- 测试模型:
- 用UserA登录,访问一个操作自身资源的接口,如
GET /api/orders/1001(假设1001是UserA的订单)。 - 在Burp中捕获这个请求。
- 在
Repeater中,将请求路径或参数中的资源ID(如1001)修改为UserB的订单ID(如1002)。 - 重放请求。
- 用UserA登录,访问一个操作自身资源的接口,如
- 关键点:服务端应该判断当前登录用户是UserA,而请求的资源
1002属于UserB,因此返回403。如果成功返回了UserB的订单数据,则存在水平越权。 - 参数不限于ID:除了路径参数,还要检查JSON请求体、URL查询参数(
?id=)、甚至自定义请求头中是否存在标识资源归属的参数。
垂直越权测试:
- 前提:你需要一个低权限账号(如普通用户)和一个高权限账号(如管理员)。通常,你只有普通用户账号,管理功能需要靠“猜”和“试”。
- 测试模型:
- 用普通用户登录,浏览其所有功能,抓取所有请求。
- 分析管理功能的可能路径(如包含
admin、manage、delete、config等关键词)。 - 直接使用普通用户的会话(Cookie/Token),去尝试访问这些管理员API。
- 或者,观察管理员API的请求格式(如果通过其他信息泄露获得),尝试用普通用户的身份构造并发送相同请求。
- 绕过前端控制:如果某个按钮前端做了灰度或隐藏,务必通过Burp直接构造其背后的API请求进行测试。前端限制形同虚设。
3.3 工具辅助与自动化
手动测试是基础,但效率有限。合理利用工具可以提升覆盖面。
- Burp Suite 插件:
- Autorize:越权测试神器。配置好低权限用户(
Auth)和高权限用户(Unauth)的请求,插件会自动用低权限用户的凭证去访问高权限用户访问过的所有接口,并标记出可能存在的越权(返回状态码差异)。 - JSON Web Tokens:用于方便地解码、修改和重签JWT令牌,测试JWT校验逻辑缺陷。
- Autorize:越权测试神器。配置好低权限用户(
- 自定义脚本:对于ID遍历测试,可以编写简单的Python脚本,配合
requests库,批量请求/api/user/[1-10000],根据返回状态码和内容长度快速定位异常点。 - 流量对比分析:使用Burp的
Compare功能,对比同一个接口在登录前/后、用户A/用户B访问时的响应差异,快速发现鉴权逻辑问题。
4. 经典案例场景深度剖析
光讲流程有点干,我们结合几个我实际遇到的、以及社区公开的典型案例,看看漏洞是怎么发生的。
4.1 案例一:调试接口泄露与未授权RCE
场景:在对一个Spring Boot应用进行测试时,目录扫描发现了/actuator路径。访问后,列出了大量端点,其中/actuator/env直接展示了应用的所有环境变量,包括数据库密码、云服务密钥等敏感信息。更重要的是,/actuator/loggers端点允许动态修改日志级别,而/actuator/heapdump可以下载内存堆转储文件。
漏洞点:Spring Boot Actuator端点未做安全加固,直接暴露在公网,且未配置独立的访问密码或将其纳入应用的安全框架管控。
利用链:
- 访问
/actuator,发现端点列表。 - 访问
/actuator/env,泄露敏感配置。 - (在某些旧版本或特定配置下)通过
/actuator/loggers端点,将某个关键类的日志级别设置为DEBUG,可能触发敏感信息打印。 - 通过
/actuator/heapdump下载堆转储文件,使用MAT等工具分析,有可能从中提取出明文密码、会话信息等。
修复建议:
- 通过
management.endpoints.web.exposure.include严格控制暴露的端点。 - 务必通过Spring Security对
/actuator路径进行访问控制,或为其配置独立的HTTP Basic认证。 - 将Actuator端口与业务端口分离,并通过网络策略限制内网访问。
4.2 案例二:基于ID递增的水平越权
场景:一个在线教育平台,学生可以查看自己的学习报告。请求格式为GET /api/student/report?report_id=5001。用户A(report_id从5001到5010)正常访问自己的报告。将report_id改为5000,成功访问到了用户B的学习报告,其中包含姓名、成绩等隐私信息。
漏洞点:服务端代码伪逻辑如下:
// 错误示范:直接使用客户端传来的reportId进行查询,未与当前用户关联 Report getReport(String reportId) { return reportRepository.findById(reportId); // 直接查询,致命! }正确的逻辑应该是:
Report getReport(String reportId, String currentUserId) { Report report = reportRepository.findById(reportId); if (report == null) throw new NotFoundException(); // 关键校验:报告的所有者是否是当前用户 if (!report.getStudentId().equals(currentUserId)) { throw new AccessDeniedException(); } return report; }挖掘技巧:对于任何涉及资源ID的操作,养成“改一改”的条件反射。不仅是GET请求,POST、PUT、DELETE请求中的ID参数同样需要测试。
4.3 案例三:JWT令牌解析与垂直越权
场景:一个系统使用JWT作为认证令牌。普通用户登录后获得一个JWT,其Payload部分包含:{“user”: “alice”, “role”: “user”}。通过观察,发现管理后台的请求地址包含/api/admin/。直接使用普通用户的JWT令牌,尝试访问GET /api/admin/user/list,返回了403 Forbidden。但是,访问POST /api/admin/system/config,却返回了200,并且成功修改了系统配置。
漏洞点:问题出在服务端的权限校验不一致。/api/admin/user/list这个接口可能通过注解或拦截器统一校验了角色必须为admin。而/api/admin/system/config这个接口的校验可能存在遗漏,或者其校验逻辑依赖于JWT中某个可以被篡改的字段(虽然JWT本身有签名,但客户端无法篡改,这里指服务端解析后信任了role字段,但该接口的校验代码漏写了)。
更深层的测试:即使服务端校验了角色,也要检查是否存在“功能权限”层面的越权。例如,管理员角色有“用户管理”和“系统配置”两个权限点。用户A是管理员,但有“用户管理”权限;用户B也是管理员,但有“系统配置”权限。如果系统只校验了角色是admin,那么用户A就能越权执行用户B的配置操作。这需要更细粒度的权限点(Permission)校验。
JWT相关测试点:
- 修改算法为none:尝试将JWT头部中的
alg字段改为none,并移除签名,看服务端是否不验证签名就接受令牌(历史漏洞,现已少见)。 - 密钥混淆攻击:如果服务端同时支持HS256(对称加密)和RS256(非对称加密),尝试将RS256公钥作为HS256的密钥来伪造令牌。
- 过期时间修改:虽然客户端不能篡改已签名的过期时间(
exp),但如果服务端时间不同步或校验逻辑有误,可能造成令牌长期有效。
5. 防御方案与安全开发建议
挖洞是为了更好的修洞。作为开发者,如何避免写出有这类漏洞的API呢?
5.1 设计阶段:最小权限与安全默认值
- 默认拒绝:所有API接口,默认情况下都应该是禁止访问的。然后通过配置,显式地允许哪些角色/用户访问哪些接口。而不是默认允许,再逐个去限制。
- 最小权限原则:给用户、服务或进程分配完成其任务所必需的最小权限。普通用户绝对不应该拥有任何管理员接口的访问权限,哪怕只是一个“只读”的管理员接口。
- 清晰的API文档与规范:在项目初期就确立API安全规范,包括必须的认证、鉴权方式,并将此作为代码审查的一部分。
5.2 实现阶段:统一的认证与鉴权中间件
- 绝不信任客户端:任何来自客户端的标识(用户ID、角色、权限列表)都只能作为参考,绝不能作为权限判断的唯一依据。服务端必须从可信的会话存储(如Redis)或已验签的Token(JWT)中重新获取当前用户的真实身份信息。
- 使用成熟的安全框架:充分利用
Spring Security、Apache Shiro、OAuth2.0等成熟框架的能力。它们提供了声明式的权限控制(如@PreAuthorize、@Secured)和灵活的配置方式。 - 实施RBAC(基于角色的访问控制)或更细粒度的ABAC(基于属性的访问控制):在关键的业务方法中,进行二次资源归属校验。
// 好的例子:在Service层进行校验 @Service public class OrderService { public Order getOrder(Long orderId) { Order order = orderRepo.findById(orderId); String currentUsername = SecurityContextHolder.getContext().getAuthentication().getName(); if (!order.getOwner().equals(currentUsername)) { throw new AccessDeniedException(“You can only view your own orders.”); } return order; } } - 对管理类、调试类接口进行网络隔离或强认证:
Actuator、Swagger、数据库控制台、运维平台等接口,不应直接暴露在公网。如果必须暴露,必须配置强密码(非默认密码)、二次验证或IP白名单。
5.3 测试与运维阶段
- 自动化安全测试:将API安全测试(如未授权、越权测试)集成到CI/CD流水线中。可以使用
OWASP ZAP、Arachni等工具进行自动化扫描。 - 定期人工渗透测试:自动化工具无法覆盖所有业务逻辑漏洞。定期邀请安全团队或外部白帽子进行深度测试。
- 全面的日志记录与监控:记录所有API访问日志,特别是失败的身份验证和权限拒绝日志。设置告警规则,对异常访问模式(如某个用户短时间内尝试访问大量非自身资源ID)进行实时告警。
- 使用API网关:在API网关层实施统一的认证、限流、黑白名单策略,为后端服务提供一个统一的安全屏障。
6. 写给SRC新手的入门指南
如果你刚接触SRC挖掘,想从API漏洞入手,这里有一些具体的建议:
- 目标选择:优先选择那些业务复杂、用户体系庞大、刚完成重构或采用微服务架构的新上线系统。这类系统由于开发节奏快、模块多,容易出现安全配置疏忽。
- 从“黑盒”开始:无需源码,专注于从外部观察和测试。熟练使用Burp Suite是你最重要的技能。把它的
Proxy、Repeater、Intruder、Scanner模块玩熟。 - 建立测试方法论:按照本文的流程:信息收集 -> 未授权测试 -> 越权测试,形成肌肉记忆。对每一个抓到的请求,都问自己三个问题:“不登录能访问吗?”(未授权)、“登录后能访问别人的吗?”(水平越权)、“普通用户能访问管理员功能吗?”(垂直越权)。
- 保持好奇心与耐心:不要只测试显而易见的
/api/user/1。多关注那些看似不起眼的端点,比如文件上传接口、消息通知接口、配置获取接口。一个/api/export接口可能导致全量数据泄露。 - 学习写高质量报告:漏洞的价值一半在发现,一半在表达。报告要清晰描述漏洞位置、复现步骤(附截图和数据包)、潜在危害,并给出具体的修复建议。清晰的报告能帮助厂商快速理解并修复问题,也能体现你的专业性。
API安全是一个纵深很长的领域,未授权和越权只是入口。通过它们,你可能会发现更复杂的逻辑漏洞、业务漏洞,甚至链式攻击。保持学习,保持好奇,最重要的是,始终保持对技术的敬畏和对安全的责任心。