查询 25 次,耗时 0.2092 秒

授权环境下的 APP/Web API 越权测试:从 BOLA 复现、证据采集到修复验证

monika

New member

monika Rep
0
0
0
Rep
0
monika Vouches
Vouches
0
2 MONTHS
2 2 MONTHS OF SERVICE
monika 获得
0
0
0
获得
0
monika 凭证
0
0
0
凭证
0
主题
8
点赞
0
HakB
2 月
在社区成长 2 月
LEVEL 1 1 XP
 
一、背景与适用范围

在 APP 和 Web 渗透测试中,业务接口越权是最常见、也最容易被误判的一类问题。典型表现是:用户登录后可以读取或修改不属于自己的订单、地址、文件、工单或个人资料。该问题在 API 场景中通常称为 BOLA(Broken Object Level Authorization,失效的对象级授权),在传统 Web 中则常被归为水平越权;如果普通用户能够调用管理员功能,则属于垂直越权。

本文只讨论本地实验、授权靶场或已获得书面许可的测试环境。示例中的地址统一使用 localhost,账号、对象 ID 和请求内容均为演示数据,不应直接用于任何真实目标。测试重点不是“猜 ID”,而是验证服务端是否在每次对象访问时重新执行授权判断,并形成可复核、可修复的证据链。

二、漏洞原理

一个安全的对象访问流程应包含四个步骤:身份认证、解析请求对象、查询对象归属关系、执行允许的操作。常见错误是只验证了“用户已经登录”,却没有验证“这个对象是否属于当前用户”。例如,服务端直接执行类似以下逻辑:

GET /api/orders/1002

后端只根据 1002 查询订单并返回结果,没有将当前登录用户的 user_id 与订单 owner_id 进行比较。攻击者在授权环境中只需要把自己的订单编号替换为另一个测试账号的订单编号,就可能读取或修改其他用户的数据。

风险不仅限于信息泄露。若修改接口也缺少对象级授权,可能进一步篡改收货地址、支付状态、审批结果或文件权限。风险等级应结合数据敏感度、是否可写、是否能批量利用以及是否影响业务流程综合判断,不能仅凭接口名称下结论。

三、前置条件

1. 准备本地或隔离的测试环境,例如自建 Flask/Node.js API、OWASP Juice Shop 的本地实例,或经过授权的测试服务器。
2. 至少准备两个普通测试账号:[email protected] 和 [email protected]。两个账号应各自拥有不同的订单、地址或工单数据。
3. 记录测试范围、允许的接口、测试时间和数据清理方式。不要使用真实个人信息、真实支付数据或生产账号。
4. 准备代理工具用于观察请求,例如 Burp Suite、浏览器开发者工具或 mitmproxy;如果测试移动 APP,应在本地模拟器中安装测试包,并确认流量来自授权环境。
5. 测试前备份数据库,尤其是涉及 PUT、PATCH、DELETE 的接口,优先使用只读验证,写操作必须使用专门的演示对象。

四、构造本地演示服务

下面是一个故意存在越权风险的伪代码示例,用于理解缺陷,不建议部署到公网:

@app.get('/api/orders/<int:eek:rder_id>')
def get_order(order_id):
if not current_user:
return {'error': 'unauthorized'}, 401
order = db.query('select * from orders where id = ?', order_id)
if not order:
return {'error': 'not found'}, 404
return order, 200

问题在于查询条件只有 id,没有使用当前会话中的用户身份。如果希望快速搭建实验,可使用 Python 虚拟环境安装 Flask:

python3 -m venv .venv
source .venv/bin/activate
pip install flask
python app.py

实际项目中不应把上述代码直接复制到生产服务。实验服务应仅监听 127.0.0.1,并使用演示数据库。例如:

app.run(host='127.0.0.1', port=5000, debug=False)

五、分步骤测试方法

步骤 1:建立基线请求

先以 Alice 登录,打开自己的订单详情,使用代理保存完整请求。重点记录 HTTP 方法、路径、Cookie 或 Authorization 头、请求体、响应状态码、响应长度以及业务字段。例如:

curl -i -H 'Authorization: Bearer ALICE_TEST_TOKEN' http://127.0.0.1:5000/api/orders/1001

确认 1001 确实属于 Alice,并保存响应作为基线。不要直接从页面显示内容推测归属,应在测试数据库或后台管理界面核对对象所有者。

步骤 2:准备第二个账号的对照数据

使用 Bob 登录,创建或查询一个独立订单,例如 2001。记录 Bob 能正常访问该对象,而 Alice 不应拥有访问权限。测试数据最好使用明显不同的字段,例如商品名称为 alice-item 与 bob-item,避免响应内容相同导致误判。

步骤 3:进行最小化的对象替换

在完全相同的请求中,仅把对象标识从 1001 替换为 2001,其他 Header、Token 和参数保持不变:

curl -i -H 'Authorization: Bearer ALICE_TEST_TOKEN' http://127.0.0.1:5000/api/orders/2001

安全实现通常应返回 403,或者为了避免资源枚举而统一返回 404。若返回 Bob 的订单详情,且响应中包含订单号、地址、联系方式等字段,可初步判定存在水平越权。若返回 401,说明是认证问题;若返回 400,可能是参数校验问题,这些结果不能直接当作越权已修复。

步骤 4:覆盖不同 HTTP 方法

对同一对象分别测试 GET、POST、PUT、PATCH 和 DELETE,但写操作只使用本地演示对象,并在操作前后核对数据库快照。例如:

curl -i -X PATCH http://127.0.0.1:5000/api/orders/2001 \
-H 'Authorization: Bearer ALICE_TEST_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"remark":"authorization-test"}'

如果 Alice 能修改 Bob 的对象,风险通常高于只读泄露。测试完成后立即回滚数据。还应检查批量接口,例如 /api/orders?ids=1001,2001、导出接口、下载接口、GraphQL 查询和 WebSocket 消息,因为开发者可能只修复了详情页接口而遗漏其他入口。

步骤 5:验证服务端而非前端的控制

删除前端按钮、修改隐藏字段或改变页面路由都不属于修复。可以在浏览器不加载页面的情况下直接发送请求,观察服务端结果。移动 APP 也应检查请求是否仅依赖客户端传入的 user_id、role 或 owner_id。客户端字段只能作为展示或业务参数,不能作为最终授权依据。

步骤 6:检查对象标识与错误处理

不要只测试连续整数 ID。授权环境中可检查 UUID、短 ID、文件名、订单号、租户 ID 和嵌套资源路径,例如 /users/2/orders/2001。标识不可猜测只能降低枚举概率,不能替代授权检查。同时比较无权对象、不存在对象和已删除对象的响应差异,避免通过状态码、响应长度或错误信息泄露对象是否存在。

六、修复方案

修复原则是把授权判断放在服务端、靠近数据访问层,并且让每种操作都明确声明权限。推荐使用带归属条件的查询:

SELECT * FROM orders WHERE id = ? AND owner_id = ?;

其中 owner_id 必须来自已验证的会话或令牌,不得直接信任请求体中的 user_id。对于修改和删除,也应使用同样的条件,并检查受影响行数:如果为零,应返回统一的无权或不存在结果,而不能继续执行后续逻辑。

伪代码示例:

@app.get('/api/orders/<int:eek:rder_id>')
def get_order(order_id):
user_id = require_login()
order = db.query_one(
'select id, status, amount from orders where id = ? and owner_id = ?',
order_id, user_id
)
if order is None:
return {'error': 'not found'}, 404
return order, 200

对于管理员、客服、租户管理员等复杂角色,建议采用集中式策略层,例如 authorize(user, action, resource),并记录授权决策日志。多租户系统必须同时校验 tenant_id 和对象 owner_id,防止用户跨租户访问。字段级权限也不能遗漏,例如用户可以查看订单,却不应看到内部成本、身份证号或支付令牌。

七、检测与回归验证

可在 CI 中建立两个测试身份,编写授权矩阵:主体包括普通用户、资源所有者、同租户非所有者、跨租户用户和管理员;动作包括读取、创建、修改、删除、导出;资源包括自己的对象、他人的对象、已删除对象和不存在对象。每次接口改动后自动运行矩阵测试。

回归用例至少应验证:1)Alice 能读取自己的订单;2)Alice 不能读取 Bob 的订单;3)Alice 不能修改或删除 Bob 的订单;4)管理员在明确授权后可以执行管理操作;5)跨租户访问被拒绝;6)接口不会从请求体 user_id 推断当前身份;7)批量、导出、下载和异步任务同样执行授权。

日志中应记录请求身份、租户、资源类型、资源标识摘要、动作、授权结果和关联请求 ID,但不要记录 Token、密码或完整敏感数据。监控短时间内大量访问不同对象 ID 的行为,并结合限速和告警降低枚举风险。

八、风险与注意事项

第一,不要在生产环境反复修改他人数据来证明问题;优先只读验证,必要的写操作必须有审批、备份和回滚方案。第二,不要把测试账号的 Token 发布到报告附件或聊天记录中。第三,不能因 ID 使用 UUID 就认为问题不存在;只要服务端缺少归属判断,攻击者仍可能通过日志、引用、批量接口或业务流程获得标识。第四,返回 404 并不一定代表修复完成,需要确认数据库查询确实包含授权条件。第五,测试移动 APP 时不要绕过证书校验攻击非授权应用,优先使用自有调试包、模拟器和本地代理配置。

九、常见问题

问:必须拿到两个用户的密码才能测试吗?
答:在授权环境中,最好由测试负责人提供两个专用账号。也可以让后端预置对照数据,但必须能够证明对象归属关系。

问:接口返回 403 就一定安全了吗?
答:不一定。还要检查同一资源的修改、删除、批量、导出、缓存和异步任务接口,并确认 403 是服务端真实授权结果,而不是前端拦截。

问:只读越权是否需要高危处理?
答:取决于泄露数据和业务影响。订单地址、手机号、内部工单、文件和个人信息都可能构成严重风险,应结合数据分类、可利用性和合规要求评估。

问:如何区分水平越权和垂直越权?
答:同角色之间访问彼此资源通常是水平越权;低权限用户调用管理员接口或执行高权限动作属于垂直越权。两者可能同时存在,应分别建立测试矩阵。

十、总结

高质量的 API 越权测试不是简单替换一个 ID,而是围绕“身份、租户、对象归属、动作和字段权限”建立完整验证链。实践时应先在授权环境中建立基线,再使用第二个测试主体进行最小化差异对比,覆盖读写、批量和异步入口,最后通过数据库条件、集中式策略和自动化回归完成修复闭环。对于 APP/Web 项目,最可靠的判断标准始终是:服务端是否在每一次对象访问前,根据可信身份重新计算授权,而不是相信客户端传来的身份、角色或对象归属信息。
 
顶部