ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

API越权漏洞自动化检测实战:基于crAPI/Hadrian/Vespasian的工具链

API越权漏洞自动化检测实战:基于crAPI/Hadrian/Vespasian的工具链 1. 整体思路与工具链设计1.1 一次真实的越权漏洞复盘API 越权漏洞自动化检测这件事听起来很高大上但如果你真正手工测试过一套上百个接口的系统就知道是个体力活。你可能需要注册三五个账号、来回切换身份、反复改请求里的用户ID还要记住每个接口的业务规则稍不留神就把一个真实存在的 IDOR 漏掉了。有一次我在一个电商系统的用户资料接口里发现把请求体中的userId从自己的改成另一个账号居然能直接读到别人的收货地址。当时的排查过程特别原始先用两个账号分别登录再拿 Burp 逐个接口替换参数。搞了一个下午只测完了二十多个接口脑子里全是“这个接口要不要带用户ID、那个ID在路径里还是在Header里”的琐碎判断。后来我把这套流程沉淀成了工具链以 OWASP 的 crAPI 作为训练靶场Hadrian 负责自动化批量扫描Vespasian 负责对告警做二次验证。这套组合适合两类人一类是刚接触 API 安全测试的实习生或开发想找个完整闭环练手另一类是已经有一定安全测试经验但不想继续靠肉眼盯流量、想把手动越权测试流程沉淀成自动化脚本的测试工程师。1.2 三条工具链的分工先讲清楚三者的定位避免后面看得一头雾水。crAPI 是一个故意内置多种漏洞的 API 靶场全称是 deliberately insecure API由 OWASP 社区维护。它模拟了一个真实业务系统里面包含了车辆登记、社区帖子、维修记录、优惠券等模块每一个模块都有对应的 API 接口。它最大的价值在于能提供一个“已知漏洞集”让检测工具跑完之后我们可以对照官方说明确认到底挖出几个、漏了几个从而评估自动化方案覆盖得好不好。Hadrian 是一个越权漏洞自动化检测引擎。它做的事情可以概括为遍历 API 接口用多账号身份交替发起请求自动替换路径参数、查询参数、请求体中的身份标识字段然后根据状态码和响应体差异来判断是否存在水平越权或垂直越权。它解决的是“量”的问题几十个接口在几分钟内就能扫完还能生成结构化的 JSON 报告。Vespasian 则是一个旁路验证器。Hadrian 报出来的越权告警我不会直接信因为自动化扫描天然会产生大量误报比如接口本身就有动态字段、不同用户看到的时间戳不同、响应里包含了随机推荐内容。Vespasian 会把这些可疑请求重新放一遍但会做更严格的响应归一化和相似度对比自动剔除掉明显不是越权的噪音最后再输出一份“确认越权”的清单。简单来说crAPI 是训练场Hadrian 是哨兵Vespasian 是裁判。三者合在一起才能从“接口很多”走到“高危告警很少”。1.3 为什么推荐这套组合而不是直接上商业扫描器市面上确实有不少商业 API 安全扫描产品导入 OpenAPI 文档点一下就能跑也会报越权风险。但用过的人都知道商业扫描器在越权检测上经常有两种极端要么告警特别少只识别出最简单的 IDOR要么告警特别多把接口里所有带 ID 的参数全当越权报出来最后你还是得手工复审。这套开源组合的思路不太一样。Hadrian 不强求自己像商业产品那样“开箱即用”它把规则引擎暴露出来让测试人员根据自己的业务接口配置身份字段位置和判定逻辑。Vespasian 又专门针对误报做了一道过滤相当于把“扫描”和“验证”两个环节拆开每个环节都可以单独调优。如果你所在团队已经有成熟的越权检测工具也可以把 Hadrian 和 Vespasian 对应到你自己的工具链位置上。重点不是这三款软件本身而是“自动枚举、交叉身份、归一化响应、二次验证”这套方法论。2. 环境准备与 crAPI 靶场部署2.1 Docker 环境准备要点整套工具链都建议用 Docker 跑crAPI 官方也提供了完整的 docker-compose 编排。部署之前先确认本机环境Docker Engine 版本不低于 20.10docker compose 插件建议用 v2 版本老版本的docker-compose命令用法差不多但 compose 文件里的一些语法可能不兼容。内存至少分配 4GBcrAPI 一共会启动多个服务包括身份认证服务、社区服务、车辆服务、数据库、邮件模拟器等内存不够的话会有容器反复重启。端口需要预留 8888、8025、8080 等。8888 是前端页面8025 是 MailHog 邮件接收界面8080 和一些内部端口在工具间通信时会用到。我踩过的第一个坑就是把 Docker 默认的虚拟内存设得太小。当时启动 crAPI 后数据库容器一直处于 unhealthy 状态页面能打开但登录总是失败。后来把 Docker Desktop 的内存从 2GB 调到 6GB再重新启动整个 compose 项目问题才消失。2.2 crAPI 一键部署与启动crAPI 的部署过程比较无脑克隆仓库后进入deploy目录直接启动就行。可以参考下面这组命令git clone https://github.com/OWASP/crAPI.git cd crAPI/deploy docker compose up -d第一次启动会拉取不少镜像如果是国内网络建议先给 Docker 配置镜像加速器。如果不配置很可能在拉取postgres、mailhog这些镜像时超时。启动成功后用下面的命令确认所有服务状态docker compose ps在这个输出里所有服务都应该处于Up状态数据库服务最好显示为healthy。如果看到某个容器不断重启先用docker compose logs 服务名看日志。最常见的原因是启动顺序竞争比如身份服务在数据库还没就绪时就尝试建表这时候直接docker compose restart 服务名一般能恢复。2.3 服务状态验证与账号注册crAPI 启动完成后浏览器打开http://localhost:8888会看到登录注册界面。先注册一个账号我习惯注册两个aliceexample.com和bobexample.com密码统一用crapi123。为什么注册两个账号越权检测的本质是两个不同身份的交叉访问。后面 Hadrian 扫描时会把 alice 的 token 和 bob 的 token 分别注入到同一个请求模板里如果 alice 的 token 能访问 bob 的资源就是越权。所以这一步别嫌麻烦至少准备两个账号。注册后 crAPI 会给邮箱发一封验证邮件。邮件界面在http://localhost:8025打开后会看到 MailHog 的收件箱找到最新一封邮件里的验证链接点击一下即可完成邮箱验证。验证之后重新登录 token 才会被身份服务正常签发。2.4 抓取 API 接口清单越权扫描的第一步是拿到完整的 API 清单。 crAPI 的各个服务都暴露了 OpenAPI 文档常见的访问路径是http://localhost:8888/openapi.json http://localhost:8888/openapi.yaml如果直接访问打不开可以尝试每个子服务的文档路径比如/identity/api/docs、/community/api/docs、/workshop/api/docs。也可以用命令行拉下来curl http://localhost:8888/openapi.json -o crapi-openapi.json拿到这份 JSON 后先不要急着扔给扫描器。花十分钟浏览一遍接口列表把明显的静态资源接口、健康检查接口、登录注册接口标记出来。这些接口通常不具备越权条件扫描器再聪明也需要你告诉它哪些接口值得测、哪些接口是纯公共接口。这个“接口分类”的步骤会直接影响后续扫描的准确率。3. Hadrian 自动化越权扫描器的部署与配置3.1 Hadrian 跑起来之前先理解它的工作方式Hadrian 的安装不算复杂可以从 GitHub Release 下载对应平台的二进制也可以直接拉 Docker 镜像。我更推荐用 Docker因为扫描器会经常升级版本管理方便一些。启动之前得先明白它内部是怎么工作的。Hadrian 会把 OpenAPI 文档里的每个 API 操作转换成一个“请求模板”。模板里既有固定的 URL、请求方法、请求头也有可变的身份令牌和参数占位符。扫描时它会对同一个模板做多轮请求第一轮用 alice 的 token 请求 alice 自己的资源得到基线响应。第二轮用 alice 的 token 请求 bob 的资源判断是否越权。第三轮用 bob 的 token 请求 alice 的资源做反向确认。所以越权检测并不能拿一个匿名 token 去扫那样只能发现未授权访问漏洞发现不了真正的水平越权。必须有两套合法身份并且知道哪个参数是资源归属者的 ID。3.2 核心配置接口文档、账号与身份参数Hadrian 的配置文件是 YAML 格式下面是我在 crAPI 上跑通的最小配置target: name: crapi base_url: http://localhost:8888 openapi: file: ./crapi-openapi.json auth: users: - name: alice username: aliceexample.com password: crapi123 - name: bob username: bobexample.com password: crapi123 token_field: access_token scan: concurrency: 5 delay_ms: 200 follow_redirects: false exclude_paths: - /auth/register - /auth/login - /health这里的重点是auth配置。Hadrian 会先自动调用登录接口换取 token所以你需要在配置里给它真实的账号密码。token_field表示登录响应里 token 字段的路径crAPI 默认返回字段是access_token。如果你的系统返回的是data.token或者token这里也要对应改。exclude_paths很重要。登录注册接口没有身份上下文不应该作为越权扫描对象。一些内部接口有特殊鉴权扫描后大概率 403除了浪费时间还会增加告警噪音所以先排除掉。3.3 越权检测规则怎么写Hadrian 不是无脑把 URL 里的数字都替换一遍它需要你提供“身份字段”的规则。你可以通过配置文件来告诉它哪些参数代表资源归属者的 ID。下面是一个水平越权规则的例子rules: - type: horizontal description: 通过路径参数替换用户ID检测水平越权 methods: [GET, PUT, DELETE] paths: - /identity/api/v2/user/{userId}/profile - /community/api/v2/posts/{postId} replace_in: path id_fields: - userId - postId - type: horizontal description: 通过请求体字段替换用户ID检测水平越权 methods: [POST] paths: - /workshop/api/v2/vehicle/{VehicleId}/update replace_in: body id_fields: - vehicleIdreplace_in可以取path、query、body表示 ID 出现在哪个位置。id_fields声明哪些字段是需要替换的。扫描时Hadrian 会拿一个用户的 token把资源归属字段替换成另一个用户对应的值。如果替换后仍然返回 200并且响应体里出现了不应出现的用户私有信息就会标记为越权。对于垂直越权规则写法类似只是不是“同一等级的不同用户”而是“低权限用户访问高权限接口”。比如普通用户尝试调用管理员删除接口rules: - type: vertical description: 普通用户访问管理员接口 methods: [DELETE] paths: - /admin/api/users/{userId} required_role: admin test_user: alice建议第一轮扫描先只配置最简单的水平越权规则等报告跑出来后再逐步加复杂度。规则越多后面的误报排查就越麻烦。3.4 扫描执行与报告解读配置文件准备完成后执行扫描hadrian scan --config hadrian.yaml --output hadrian-report.json扫描过程中会实时打印每个接口的请求状态。第一次在 crAPI 上跑大概几分钟就能结束如果接口数量特别多可以先把concurrency调低避免被目标系统限流。生成的hadrian-report.json里每个告警大致包含以下字段字段含义api存在越权风险的接口路径和方法rule命中的规则类型horizontal 或 verticalreq_user实际发起请求的用户victim_id被替换的资源归属者 IDstatus_code替换 ID 后的响应状态码risk_score风险评分0 到 10evidence响应体里的关键证据片段拿到告警后不要急着确认大部分自动化扫描器的第一轮输出都是“高误报、真阳性混杂”。这时候就该 Vespasian 出场了。4. Vespasian 旁路验证与误报治理4.1 为什么还需要一个独立的验证器你可能会问Hadrian 不是已经输出越权风险了吗为什么还要再来一套 Vespasian因为第一轮扫描的结果往往会被两类情况污染。第一类是动态响应。很多接口会在响应体里塞时间戳、在线状态、随机推荐列表同一份资源在不同用户视角下本来就不完全相同。Hadrian 如果用“响应体长度不一致”来判断很容易把这些动态差异当成越权证据。第二类是业务设计本身。比如某个帖子接口允许所有登录用户查看那 alice 能看 bob 的帖子就是预期行为不是越权。自动化工具很难理解业务语义只能靠验证器做更冷静的对比。Vespasian 做的事情很聚焦拿到 Hadrian 的告警列表重新跑一遍可疑请求但这次不再只比状态码和响应长度而是做响应归一化、敏感字段提权判断、会话一致性校验。只有经过它确认的告警才会进入最终报告。4.2 部署 Vespasian 并接入 Hadrian 报告Vespasian 同样提供 Docker 镜像启动命令可以这样写docker run -v $(pwd):/data vespasian/vespasian verify \ --report /data/hadrian-report.json \ --target http://localhost:8888 \ --config /data/vespasian.yaml它的输入是 Hadrian 的 JSON 报告输出是一个新的验证报告vespasian verify --report hadrian-report.json --output vespasian-report.json在vespasian.yaml里需要配置好两套用户的 token 获取方式和资源归属字段的上下文关系auth: alice: login_url: http://localhost:8888/identity/api/auth/login username: aliceexample.com password: crapi123 bob: login_url: http://localhost:8888/identity/api/auth/login username: bobexample.com password: crapi123 verify: dynamic_fields: - createdAt - updatedAt - lastLogin - token need_evidence_in: - email - phone - addressdynamic_fields列的是响应体里需要忽略的动态字段need_evidence_in表示如果响应体里出现这些字段就可以作为“受害用户私有数据泄露”的强证据。比如 bob 的接口返回里出现了 alice 的email这基本可以确认越权。4.3 二次验证的判定逻辑再讲透一点Vespasian 在二次验证时的核心逻辑可以拆成三步。第一步构造“同账号同资源”的对照组。用 alice 的 token 请求 alice 自己的资源得到响应 A。这是正常基线。第二步构造“异账号同资源”的测试组。用 bob 的 token 请求 alice 的资源得到响应 B。如果 B 返回 401/403说明接口有权限控制告警直接关闭。第三步对 A 和 B 做归一化比较。先把响应体里所有dynamic_fields字段的值抹掉再把数字、时间戳、UUID 统一替换成占位符最后计算结构相似度。如果 B 的结构和 A 高度相似并且 B 里包含 alice 的email、phone等私有字段Vespasian 才会把这条告警标记为“confirmed”。这种做法虽然保守但非常实用。它牺牲了少部分“结构长得不像但确实越权”的漏报换来了整份报告的高可信度。对于要交给业务方或开发团队的越权报告宁可漏掉几条疑似项也不能让假阳性把报告的公信力毁掉。4.4 把 Vespasian 接入 CI/CD 的注意点当你在 crAPI 上把整套流程跑通、确认告警可信之后自然想把这条流水线接到公司项目的 CI/CD 里。我的建议是先接定时任务再接代码提交触发最后再考虑合并请求阻塞。第一次接入时把扫描频率设为每天凌晨一次只发告警不阻断。这样可以观察一段时间看看误报率是否控制在可接受范围内。等到 Vespasian 确认过的历史告警和人工复核结果基本一致再调整成主干分支变更时自动扫描并允许在出现高危越权时让流水线失败。在 CI/CD 里运行还有个细节扫描器需要访问部署环境而部署环境往往有网络隔离。最简单的方式是让扫描任务跑在与目标环境同一个内网的独立容器里避免把 Token 明文写到流水线日志里。可以用 CI 的 secret 机制管理登录密码扫描结束后把报告归档到制品库而不是直接打印到日志。5. 常见问题与排查技巧实录5.1 快速排查表部署和运行过程中有几个问题我几乎每次都会遇到整理成一张速查表方便你对照排查。症状可能原因处理方法容器不断重启Docker 内存不足调大 Docker 内存重启 compose 项目页面打不开端口无响应容器还在初始化docker compose logs查看启动日志注册后收不到邮件MailHog 端口被占用确认 8025 端口未被其他程序占用登录返回 401邮箱未验证打开 MailHog 点击验证链接后重新登录OpenAPI 文档拉不下来子服务文档路径不同逐个服务试/api/docs、/openapi.json扫描时大量超时并发太高调低concurrency增加delay_ms告警很多但都是假阳性未归一化动态字段在 Vespasian 里补充dynamic_fields5.2 深入排查 400 invalid schema 错误在扫描过程中你可能会看到类似api error: 400 invalid schema for function artifact这样的报错。这个报错大多不是目标系统有问题而是 Hadrian 在解析 OpenAPI 文档里的 schema 时发现请求体结构不符合文档定义。原因通常有两个。第一crAPI 的 OpenAPI 文档里有些接口的参数格式写得不严格比如createdAt字段既可能是字符串也可能是时间戳工具按文档生成的请求体会被后端校验器拒绝。第二扫描器自动填充的字段值不符合文档里的正则约束常见于要求指定格式的 ID 字段。处理办法是手动维护一个请求模板覆盖文件。在 Hadrian 配置里指定模板目录针对报错的接口手动写请求体而不是依赖工具自动生成。虽然麻烦一点但能显著提高扫描覆盖率。如果你对接口本身没有alteration权限那就先跑通工具再逐步把模板补齐不要因为一两个接口报错就放弃整轮扫描。5.3 降低误报率的一些取巧方法除了在 Vespasian 里配dynamic_fields我还有一些降低误报的小技巧。第一个技巧是给每个测试账号准备一套“标志性数据”。注册 alice 时在收货地址里写一串唯一字符串比如ALICE-MARKER-2024。同样给 bob 也写一个唯一标识。后续扫描时只要在响应体里看到对方的标志性字符串基本可以确认越权成立比用正则匹配邮箱更稳定。第二个技巧是开启响应结构比对时先把响应体里的所有 URL、图片地址、版本号统一替换成占位符。因为很多系统的导航栏和推荐内容就是因用户而异的不看这些字段只看业务数据本身能滤掉一大半假阳性。第三个技巧是“先只测读接口再测写接口”。第一轮扫描只配置 GET 接口的越权规则等把读接口的告警全部验证干净再逐步加入 POST、PUT、DELETE。这样即使写接口造成了脏数据影响范围也更可控排查告警时不需要同时处理数据污染问题。5.4 需要避开的几个思维误区自动化越权检测最容易犯的错是把它当成一次性的“漏洞扫描器”。实际上越权漏洞是跟业务强相关的同一个接口在不同版本、不同角色模型下判定结果会完全不一样。Hadrian 和 Vespasian 只是把你的验证逻辑自动化了业务规则还是得靠人来配置。另一个误区是只关注水平越权忽略垂直越权。很多团队会在 Hadrian 里配一堆{userId}、{orderId}的替换规则却忘了检查普通用户是否能调用管理员接口。其实垂直越权的危害往往更大一次普通的注册账号就能拿到全站用户数据。建议在规则里把required_role场景单独列出来即使读接口扫不出问题写接口和管理接口也要测一遍。还有一点自动化检测的最终输出不应该是“一堆告警”而应该是“一份可执行的修复清单”。每个 confirmed 告警最好都附上请求样例、证据响应、建议修复方式比如“在服务端校验资源归属使用PreAuthorize或中间件统一做对象级授权检查”。否则开发团队收到报告后还是要花大量时间复现和定位安全测试的价值就打了折扣。最后再聊两句实际体会我在 crAPI 上把这套流程跑了不下二十遍最大的感受是自动化检测真正解决的不是“发现所有越权”这个最终目标而是把大量重复的、机械的请求替换工作压缩到几分钟内完成。Human 的精力被释放出来后才有时间去看那些真正需要业务判断的告警比如“这个接口为什么允许跨用户访问”“管理员角色到底覆盖哪些接口”。如果你现在刚上手第一轮扫描先把并发调低规则先少后多接口先读后写。宁可第一次跑慢一点也要把假阳性的基数控制住。等你在 crAPI 上把工具链整个跑顺再带着这套方法论去审视自己业务系统的 API 权限模型会突然发现很多之前被忽略的风险点。这个从“手工试”到“工具化”再到“业务化”的过程才是 API 越权检测最值得投入时间的地方。
返回列表