ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

API越权漏洞自动化检测:Hadrian+Vespasian+crAPI实战指南

API越权漏洞自动化检测:Hadrian+Vespasian+crAPI实战指南 API 越权漏洞在 OWASP API Security Top 10 里已经连续多年霸榜第一BOLA对象级授权缺失就是大家常说的水平越权和 BFLA函数级授权缺失垂直越权几乎是企业应用被攻击的第一入口。和 SQL 注入这类“报错式”漏洞不同越权漏洞不发错误、不报异常接口正常返回 200数据却悄悄换了主人所以开发自测很难发现等被人遍历刷库往往已经晚了。我今年在好几个项目里用 Hadrian Vespasian crAPI 这套组合做 API 越权自动化检测把过去靠手工两个账号来回切换才能发现的 IDOR 问题变成了“扫描器自动枚举目标验证器自动确认并出 PoC”的半自动流水线。这篇文章重点讲这套环境怎么搭、检测原理是什么、完整走一遍流程再把我在部署和排查过程中踩过的坑都列出来。适合做 API 安全测试的工程师、准备实战练习的开发者以及想在自己团队里落地 API 安全检测的同学参考。1. 项目整体思路与工具选型解析1.1 越权漏洞为什么难发现先用一个最典型的场景说明什么是越权。假设一个在线购物系统用户 A 登录后访问订单详情接口GET /api/v1/orders/1001服务端返回订单 1001 的数据。如果这个接口只信任请求参数里的订单号却没有校验“这个订单号属于当前登录用户”那么我把订单号改成 1002、1003、1004……就能遍历出别人的订单信息。这就是水平越权也叫 IDORInsecure Direct Object Reference或 BOLA。垂直越权更好理解普通用户直接请求管理员接口比如GET /api/v1/admin/users。如果服务端只检查了“是否登录”没有检查“是否有管理员角色”低权限用户就能拿到管理数据。这两类漏洞难发现的核心原因有三个。第一接口响应是 200没有任何“非法访问”的告警特征传统 Web 扫描器基本无能为力。第二站在开发视角功能完全正常自测根本不会想到换一个身份去访问。第三人工验证需要至少两个不同权限的账号来回切换耗时极长而且漏一个参数就漏一个洞。所以这类漏洞必须靠自动化手段来做“差分对比”才能把问题批量暴露出来。1.2 Hadrian、Vespasian、crAPI 三件套的分工这套方案里三个角色各司其职缺一不可。crAPI 是 OWASP 社区维护的一个故意设计得“千疮百孔”的 API 靶场应用全称 Completely Ridiculous API。它模拟了一个完整的车辆管理业务包含用户注册、登录、车辆绑定、位置上报、优惠券兑换、社区发帖等功能内置了大量真实业务里常见的 API 漏洞。它解决的是“目标从哪来”的问题——我们不可以在真实生产环境里跑越权扫描但可以在完全合法的靶场环境里反复练习把流程跑通再迁移到自己的测试环境。Hadrian 是本方案里的自动化越权扫描引擎负责从 OpenAPI 规范或流量文件中梳理 API 端点自动构造“跨账号访问”的测试用例把疑似越权的请求成批打出去并标记异常。你可以把它理解成一台“接口请求枚举器”它不负责判断业务是否合法只负责把两个账号的资源交叉访问情况全部列出来。Vespasian 则是验证引擎负责对 Hadrian 发现的疑似越权请求做二次确认。它能自动携带新的 Token 发起基准请求和越权请求对比响应体中的关键数据是否一致确认存在越权后生成标准化的 PoC 报告。它解决的是 Hadrian 误报率偏高的问题——扫描器负责广撒网验证器负责精确打击验证器还需要处理并发、Token 刷新、响应比对等细节。1.3 选型考量为什么不用纯商业扫描器可能有人会问商业 DAST 或 API 安全平台不是也能测越权吗我在实际对比之后发现这套开源组合有几个无法替代的优势。商业扫描器第一个痛点是贵按资产和扫描次数计费想要在内部测试环境里高频地跑、每次迭代都扫一遍成本很难接受。第二个痛点是黑盒扫描器大多依赖漏洞特征库对 SQL 注入、XSS 这类有“固定攻击模式”的漏洞识别能力强但越权是逻辑漏洞特征库很难描述“什么数据属于哪个用户”所以商业扫描器在这个场景下表现普遍不理想。第三个痛点是策略不透明出了问题想排查扫描逻辑都没法下手。而 Hadrian Vespasian 的思路本质上是“差分对比”——把同一个动作放到两个不同身份下执行比较响应差异。这是检测越权最朴素也最有效的方式不依赖漏洞特征库逻辑完全可控而且开源可修改。crAPI 作为靶场让整套流程可以在不影响线上环境的前提下反复验证。三件套拼起来就是一套贴着“越权检测”这个具体问题定制的流水线比通用商业工具更适合解决这一类问题。2. 环境准备与部署完整流程2.1 crAPI 靶场搭建先把 crAPI 跑起来。官方推荐用 Docker Compose 方式部署前提是你本机已经装好 Docker 和 Docker Compose版本不要太老Docker 20.10 以上、Compose 2.x 基本没问题。git clone https://github.com/owasp/crapi.git cd crapi docker-compose up -d第一次启动需要拉取镜像耗时取决于网络状况。启动完成后用docker-compose ps查看容器状态正常情况下你会看到几个核心容器crapi-identity认证服务负责注册、登录、Token 签发crapi-community社区模块处理车辆、位置、订单等业务接口crapi-workshop交互式教学组件可以查看漏洞提示mailhog模拟邮件服务用于接收注册验证邮件mongo数据库打开浏览器访问http://localhost:8888就能看到 crAPI 的界面。注册时需要邮箱、密码、手机号密码复杂度要求比较高8 位以上且必须包含大小写字母、数字和特殊字符。注册后验证邮件会发送到 MailHog在浏览器打开http://localhost:8025就能看到收到的信点击验证链接即可完成激活。这里有两个高频坑。第一是 8888 端口被占用解决办法是改docker-compose.yml里的端口映射比如改成8889:8888改完要docker-compose down再docker-compose up -d。第二是部分环境中容器启动顺序有问题Mongo 还没就绪时业务容器就起来了导致接口报错解决办法是先停掉再启动或者手动把业务容器docker-compose restart crapi-community crapi-identity重启一遍。2.2 安装并配置 Hadrian 扫描引擎Hadrian 以 Python 命令行工具分发支持通过 pip 安装也支持 Docker 方式运行。建议直接用 pip方便查看和修改源码pip install hadrian-scanner安装完成后先初始化工作目录和配置文件hadrian init --workspace ./api-scan这一步会在当前目录生成一个hadrian.conf配置文件里面核心字段有这么几个target.base_url目标 API 的 Base URL比如http://localhost:8888spec.pathOpenAPI 规范文件的本地路径auth.token_endpoint获取 Token 的认证接口地址auth.credentials多套测试账号的凭证列表支持定义多个用户engine.concurrency并发请求数默认 10调大会更快但更容易触发对方限流配置好后可以先用hadrian doctor检查配置是否有误它会自动尝试连接目标地址和 Token 接口确认环境可达。2.3 安装并配置 Vespasian 验证引擎Vespasian 负责结果验证和 PoC 生成同样用 pip 安装pip install vespasian-validator初始化工作区vespasian init --workspace ./vesp-workspaceVespasian 的输入是 Hadrian 导出的 JSON 扫描结果它会读取每个疑似越权端点重新发起请求做验证。它需要一个验证规则文件描述响应中哪些字段是“关键字段”用于判定两个请求是否命中了同一资源。默认规则会匹配常见的业务字段比如id、email、phone、username、vehicleId等也可以按业务自定义。2.4 三件套之间的联动配置环境搭好之后最核心的就是把三件套串成一条流水线。第一步是从 crAPI 导出 OpenAPI 文档这是 Hadrian 识别端点的基础。crAPI 的接口文档可以通过 swagger 页面访问也可以直接用 curl 拉取 JSONcurl -s http://localhost:8888/api/docs-json crapi-openapi.json拿到 OpenAPI 文档后配置 Hadrian 执行扫描hadrian scan \ --spec ./crapi-openapi.json \ --base-url http://localhost:8888 \ --users alice:Passw0rd123,bob:Passw0rd123 \ --output ./scan-results.json扫描结果出来之后交给 Vespasian 验证vespasian verify \ --input ./scan-results.json \ --base-url http://localhost:8888 \ --users alice:Passw0rd123,bob:Passw0rd123 \ --output ./verified-report.html这一步的关键在于两个工具的账号信息必须完全一致否则验证阶段无法复现扫描阶段的行为。我习惯把账号密码写在环境变量里避免在 shell 历史记录中暴露明文。3. 越权检测核心原理与实现逻辑3.1 水平越权检测跨用户资源访问对比水平越权检测的底层思路是“差分对比”核心流程可以拆成四步。第一步用账号 A 登录遍历 OpenAPI 文档中所有涉及资源 ID 参数的端点记录每个请求的完整请求包和响应包。第二步用账号 B 登录拿到账号 B 的 Token。第三步把账号 A 的请求中的 Token 替换为账号 B 的 Token同时把资源 ID 替换为账号 A 的资源 ID。第四步如果响应中返回了账号 A 的资源数据就说明接口在授权校验上存在漏洞——因为账号 B 本不该看到账号 A 的数据。整个流程的关键在于“哪些参数是资源 ID 参数”。Hadrian 的做法是结合 OpenAPI 的参数名与路径语义来做推断路径中出现{id}、{orderId}、{vehicleId}、{userId}这类占位符或者参数名包含id后缀的都视为高危候选参数。这种启发式方式肯定不如人脑精准但作为第一轮“广撒网”非常有效误报交给 Vespasian 和人工复核去处理。3.2 垂直越权检测角色权限边界验证垂直越权的检测思路和水平越权不同核心是角色权限边界验证。Hadrian 需要至少一个低权限账号和一个高权限账号。具体流程是先用高权限账号登录遍历接口获得高权限端点清单然后切换低权限账号的 Token重新请求这些高权限端点对比响应状态码和响应体。如果低权限账号请求管理员接口时返回的不是 401/403 而是 200并且响应中带有敏感业务数据就构成垂直越权。这里有一个容易被忽略的细节很多系统对“未登录”做了鉴权却对“登录了但权限不够”没做鉴权只要请求里有合法 Token 就放行。所以垂直越权检测必须用一个真实可用的低权限账号而不是直接用未登录状态去测否则测不出真实问题。3.3 疑似越权的评分与排序逻辑HAdrian 扫描出的结果不会全报它会根据证据强度给每个疑似越权点打分。实际项目中我总结出的主要评分因子有四个。第一是响应状态码如果跨用户访问返回 200强信号返回 403/401 则直接排除。第二是响应体内容匹配度对比请求账号 B 的 Token 访问账号 A 的资源时响应体与账号 A 原始响应体的相似度相似度高于阈值才判定为命中。第三是关键业务字段校验响应体中是否包含账号 A 独有的数据字段比如 email、手机号、车辆序列号等。第四是数据唯一性如果返回数据是所有用户共有的公共数据比如系统公告、公共商品列表则不是越权。这个阶段宁可多报不能漏报因为后续还有 Vespasian 会做二次筛选。我的经验是把相似度阈值调低到 0.7 左右让 Hadrian 多给一些候选交给验证器去精确判断。3.4 Vespasian 的二次验证与 PoC 生成策略Vespasian 拿到 Hadrian 的扫描结果后会依次做三件事。第一是去重合并。同一端点、同一个资源 ID 在批量遍历中可能被标记多次Vespasian 会按“端点 参数名 越权类型”合并避免最终报告里出现大量重复项。第二是重新验证。它会丢弃扫描阶段的缓存响应用新的 Token 发起一次完整的“基准请求 越权请求”对比排除扫描期间因为网络抖动、限流或临时数据变更导致的假阳性。第三是生成 PoC。验证确认存在越权后Vespasian 会生成包含完整请求方法、路径、Header、Body 的 PoC 示例并附上修复建议方便直接提交给开发修复。这套二次验证机制非常重要。我见过不少团队直接用扫描器结果提工单结果开发一复核发现是误报信任度就垮了。加上 Vespasian 这一层提给开发的越权报告基本可以达到“直接可复现”的程度。4. crAPI 靶场越权检测完整实操实录4.1 准备两个测试账号与测试数据在 crAPI 里注册aliceexample.com和bobexample.com两个账号密码统一设置为Passw0rd123。注册时填写不同的手机号和邮箱方便后面验证越权响应中的字段差异。账号激活后登录账号 A 做一些业务操作创建车辆、下订单、绑定信用卡确保账号 A 下有独立的业务数据。这一步很关键因为差分对比需要有“账号 A 独有的数据”作为参照物。如果两个账号都是空数据即使发生越权响应体对比也看不出差别。通过 API 登录获取 Token后面手工复核时会用到curl -s -X POST http://localhost:8888/identity/api/auth/login \ -H Content-Type: application/json \ -d {email:aliceexample.com,password:Passw0rd123}返回的access_token就是后续请求的身份凭证。实际使用中我会把 Token 保存到环境变量方便后续直接引用。4.2 用 Hadrian 执行越权扫描信息准备齐全后执行扫描命令hadrian scan \ --spec ./crapi-openapi.json \ --base-url http://localhost:8888 \ --users alice:Passw0rd123,bob:Passw0rd123 \ --output ./scan-results.json扫描器会先解析 OpenAPI 文档中的所有路径识别候选 ID 参数然后对不同端点发起跨账号请求。整个过程会根据端点数量和并发配置持续几分钟crAPI 这种规模的靶场大约 3 到 5 分钟能跑完。扫描结果的 JSON 文件里每条疑似越权记录会包含端点、参数、用户对和证据信息{ id: IDOR-001, endpoint: GET /community/api/v2/vehicle/{id}, param: id, userA: aliceexample.com, userB: bobexample.com, status: suspected, evidence: { status_code: 200, response_body_match: 0.96 } }response_body_match超过 0.9 就意味着跨账号请求拿到了几乎一样的数据属于高度疑似越权需要重点验证。4.3 用 Vespasian 二次确认越权并生成报告扫描完成后把结果交给 Vespasianvespasian verify \ --input ./scan-results.json \ --base-url http://localhost:8888 \ --users alice:Passw0rd123,bob:Passw0rd123 \ --output ./verified-report.htmlVespasian 会对每条疑似记录重新执行请求并对比响应中的关键字段。验证完成会生成一份 HTML 报告包含越权端点列表、涉及参数、请求样例、证据截图描述和修复建议。以 crAPI 的车辆查询接口为例Vespasian 会先请求GET /community/api/v2/vehicle/{id}获取账号 A 的车辆信息然后使用账号 B 的 Token 请求同一个车辆 ID如果响应中返回账号 A 的车辆序列号报告就会判定为“确认越权”并给出完整 PoC 请求。我自己在实操中验证过这类对象级 IDOR 在 crAPI 里命中率很高。4.4 人工复核 PoC 请求的必要步骤自动化工具的输出不能直接信人工复核是最后一道关。复核方法很简单从 Vespasian 报告中挑出标注“确认越权”的端点用 curl 手动执行一次。以车辆查询接口为例我会这样复核# 使用账号 B 的 Token 请求账号 A 的车辆 ID curl -s http://localhost:8888/community/api/v2/vehicle/1 \ -H Authorization: Bearer $BOB_TOKEN如果响应中出现了账号 A 的车辆数据确认越权成立。如果返回的是空对象或者公共默认数据则属于误报需要从结果中剔除。复核时有一个重要原则不能只看响应状态码和响应长度要对比响应体中的业务字段。有些接口返回的数据是所有用户共享的公共数据比如系统配置、公共商品列表、默认头像地址这类响应即便状态码是 200也不能算越权。我在实际项目中遇到过不少这种“假阳性”基本都是因为复核时偷懒没有逐字段对比。5. 常见问题与排查技巧实录5.1 部署阶段高频问题与解决方案部署阶段的问题集中在 Docker 环境和端口配置上我整理了一个速查表问题现象可能原因解决方案8888 端口访问不了端口被其他服务占用修改docker-compose.yml端口映射后重启MailHog 收不到验证邮件容器启动顺序错乱执行docker-compose restart mailhog后重新注册注册提示密码不合格密码强度不够使用类似Passw0rd123的强密码接口返回 500Mongo 未就绪查看docker-compose logs重启业务容器Docker 版本过老低版本不支持部分配置升级 Docker 到 20.10 以上其中容器启动顺序的问题最容易误导人。crAPI 的 Compose 文件默认不处理服务依赖关系Mongo 还没完成初始化时业务容器就开始启动于是接口报错。解决办法是docker-compose restart重启业务组件或者先启动数据库等它就绪后再启动业务容器。5.2 扫描阶段常见故障扫描阶段的故障主要集中在 OpenAPI 文档导入和 Token 认证上。第一个高频问题是 OpenAPI 文档导入失败报 schema 格式错误。原因通常是 crAPI 的文档版本与 Hadrian 解析器的兼容性问题或者文档中包含了非标准的扩展字段。解决方法是先用hadrian doctor --spec ./crapi-openapi.json做一次解析检查确认文档能被正确读取。第二个高频问题是 Token 过期导致误报成片出现。Hadrian 扫描时长超过 Token 有效期后后续请求会拿到 401但扫描器可能把这些 401 误判为“权限控制失效”或反过来漏掉真实问题。解决方法是确保账号凭证有刷新机制或者在扫描前调长 Token 有效期。crAPI 这类靶场默认 Token 有效期较长基本不会触发这个问题但迁移到真实测试环境时一定要检查认证过期策略。第三个问题是扫描速度过慢。越权检测需要对每个候选接口做多账号交叉请求请求数量是端点数量的数倍。如果接口数量多需要调整并发数但并发太高容易触发目标限流得不偿失。我一般先用并发 5 跑少量接口验证连通性再逐步调高到 10 到 20。5.3 结果误报与漏报处理思路越权扫描的结果不可能一次就干净误报和漏报都需要从机制上想办法。误报的根源在于“响应数据相似”不一定等于“越权访问成功”。最常见的场景是接口返回了公共默认数据或者资源在另一个用户下不存在时返回了空对象导致响应体相似度极高。处理办法是在 Vespasian 的验证规则里增加“白名单字段”过滤把系统公告、公共配置、默认头像等公共字段排除在相似度计算之外或者要求响应中必须包含当前资源所有者的唯一标识字段才判定命中。漏报的根源更隐蔽。一种是接口的对象 ID 不在路径中而是在请求体里比如POST /api/order/detail的请求体里带{order_id: xxx}Hadrian 可能没有把请求体参数识别为越权参数。另一种是需要特定业务前置条件的越权比如必须先创建订单才能通过售后接口访问他人订单单请求自动化无法触发整个业务链路。处理办法是配合手工测试补全私有 Flow或者写自定义验证脚本把多步业务请求串起来再套用差分对比逻辑。5.4 拓展把这套流程接入 CI 流水线在团队里落地这套方案最有价值的动作是把它接入 CI/CD 流水线实现对测试环境的每日自动扫描。以 GitLab CI 为例可以定义一个定时任务每天早上拉取最新的 crAPI 或目标测试环境地址执行 Hadrian 扫描和 Vespasian 验证最后把报告上传到制品库并通知相关开发。关键配置点是扫描环境必须隔离不能影响正常开发测试建议使用独立的测试账号和独立的测试数据。接入 CI 后我还有一个经验不要一发现越权就阻塞流水线最好先记录到缺陷库由安全负责人和开发确认后再决定是否阻塞发布。越权修复通常涉及服务端授权逻辑改造不是一次提交能解决的直接阻塞会导致团队对工具产生抵触情绪反而推不动安全改进。6. 实操心得与后续扩展方向整套流程跑通之后我对越权检测这件事有了几个比较深的体会。第一越权检测的本质是身份问题不是漏洞特征问题。任何越权漏洞的背后都是服务端没有正确建立“当前用户身份”和“被访问资源所有者”之间的关联校验。所以自动化工具再强也需要先把用户模型、角色模型梳理清楚否则工具不知道哪些数据属于谁扫描自然无从谈起。第二双账号差分对比是这套方案的核心但有一个前提两个账号要有足够的差异化数据。我在 crAPI 里注册的两个账号都会创建不同的车辆和订单就是为差分对比准备参照物。如果账号数据太少或者完全对称越权发生时响应体差异不明显很容易漏报。第三自动化是“常跑”的工具不是“替代人”的工具。越权检测这种逻辑漏洞自动化擅长的是大规模枚举和模式化对比而业务语义的理解、行业特性的判断、漏洞危害的评估仍然需要人来完成。我建议把人工复核作为强制环节写进流程宁可多花十分钟也不能把未经确认的越权报告发给开发。后续这整套方案还可以往两个方向扩展。一个是接入实时 API 流量录制把线上流量脱敏后导入 Hadrian让扫描覆盖的范围从 OpenAPI 文档扩展到实际生产请求能发现文档里没有暴露的隐藏接口。另一个是扩展 Vespasian 的自定义验证插件针对不同业务场景编写专门的验证逻辑比如验证订单越权、文件下载越权、短信轰炸等特定类型的授权问题。工具是死的思路是活的把差分对比这个核心思路用到极致能解决的安全问题远比今天写的这套流程更多。
返回列表