发布于57分钟前57分钟 这个问题在授权测试中比较常见:测试账号只能访问普通业务功能,但通过修改请求路径、参数或令牌中的角色字段,可以读取管理接口数据;进一步地,应用服务器还可能具备访问内网管理面的能力。排查重点不是“能不能打进去”,而是确认权限边界、访问链路、日志证据和修复是否闭环。一、背景与测试范围以一套典型的前后端分离系统为例:外部入口:Nginx 反向代理,提供 HTTPS 和统一访问日志。业务服务:Java 或 Node.js API,运行在独立应用服务器上。身份认证:JWT,普通用户角色为 user,管理员角色为 admin。内网资源:仅允许应用服务器访问的运维面板和数据库网段。测试前应明确授权边界,包括目标域名、测试账号、允许的时间窗口、禁止访问的生产数据以及是否允许验证内网连通性。内网验证优先使用 TCP 连接和固定的无害 HTTP 请求,不进行密码爆破、批量扫描或数据下载。二、问题定位:先确认是认证问题还是授权问题常见误区是看到接口返回 403 就直接判断“权限控制正常”。实际应区分以下情况:认证失效:未携带令牌或令牌过期,通常返回 401。授权失败:身份有效,但角色没有访问资源的权限,通常返回 403。对象级授权缺失:接口返回 200,但普通用户可以读取其他用户、订单或租户的数据。仅前端限制:按钮被隐藏,但后端接口仍然接受请求。建议使用同一个普通测试账号,对比前端正常操作和直接请求接口的结果。测试数据应使用专门创建的标记对象,例如名称中加入 AUTHZ_TEST_2025,避免触碰真实业务数据。三、权限边界验证步骤1. 建立基线请求先记录普通用户正常访问个人资料接口时的请求方法、路径、请求头和响应状态。不要直接复制管理员令牌到共享终端或工单中。curl --request GET ' \ --header 'Authorization: Bearer <ordinary-user-token>' \ --header 'Accept: application/json' \ --include随后选择一个明确属于管理员的接口,例如用户管理列表。验证时只关注状态码、响应字段和服务端日志,不要求导出完整数据。curl --request GET ' \ --header 'Authorization: Bearer <ordinary-user-token>' \ --header 'Accept: application/json' \ --include如果普通用户收到 200,并返回了用户标识、邮箱或角色信息,应记录为越权访问候选问题。若返回 403,还要确认后端是否真正拒绝了请求,而不是仅返回一个空数组。2. 验证对象级访问控制对象级问题通常表现为修改路径中的资源编号后,能读取其他对象。例如普通用户创建测试订单后,将自己的订单 ID 改为同租户或其他租户的测试 ID,比较返回结果。验证时只使用两个预先创建的测试账号和测试对象。# 账号 A 访问自己的测试对象 curl -sS ' \ -H 'Authorization: Bearer <user-a-token>' # 账号 A 尝试访问账号 B 的测试对象 curl -sS ' \ -H 'Authorization: Bearer <user-a-token>'判断标准不能只看 HTTP 状态码。需要确认响应中的 owner_id、tenant_id、订单状态等字段是否属于另一个账号。若接口返回固定格式的空对象,也应结合应用日志确认服务端是否执行了对象查询。四、JWT 和后端授权实现的检查要点JWT 解码只能用于查看令牌结构,不能把修改后的令牌当作有效凭证。验证签名算法、签发方和受众时,应在授权环境中使用服务端配置和测试令牌进行比对。服务端必须校验签名、过期时间、iss 和 aud,不能仅解析载荷中的角色字段。角色判断应在后端中间件或服务层完成,不能依赖前端路由守卫。高风险操作应再次校验资源归属和操作权限,不能只在网关校验一次。权限变更和账号禁用后,应考虑令牌撤销、短时效令牌或服务端会话校验。在代码审查中,可以重点搜索类似“从请求参数直接读取用户 ID”的逻辑。正确做法是从已验证的身份上下文中获取当前用户,再把资源归属作为查询条件。// 不安全:用户可通过请求参数替换 userId const user = await userDao.findById(req.query.userId); // 较安全:身份来自已验证的会话上下文,并同时限制租户范围 const currentUser = req.auth.subject; const tenantId = req.auth.tenantId; const user = await userDao.findOne({ id: req.params.userId, tenantId: tenantId }); if (!user || user.id !== currentUser && !req.auth.permissions.includes('user:read:any')) { return res.status(403).json({ error: 'forbidden' }); }五、内网访问链路的安全验证如果应用服务器能够访问内网管理面,不能直接把这视为漏洞。需要证明存在“外部低权限身份到内部高权限资源”的实际边界穿透,并记录每一跳的身份和网络位置。在获得明确授权后,可在应用服务器或经批准的测试代理上对指定内网地址做最小化验证:# 仅验证指定端口是否建立 TCP 连接,不进行端口扫描 nc -vz -w 3 10.10.20.15 8443 # 仅请求健康检查或版本接口,禁止访问管理数据和执行操作 curl --max-time 5 --noproxy '*' \ -H 'X-Assessment-ID: AUTHZ-TEST-001' \ ' URL 作为测试目标。报告中应记录:入口请求、应用服务器源地址、目标地址、目标端口、响应时间、是否需要内部凭证以及是否产生服务端审计日志。六、日志留痕与关联分析权限问题的修复不能只看接口返回 403,还要确认拒绝行为可审计。至少检查以下日志:Nginx 或网关:请求时间、源 IP、请求路径、状态码、请求 ID。应用日志:认证主体、租户、权限判定结果、资源 ID、拒绝原因。身份系统:令牌签发、刷新、撤销、账号禁用和角色变更。内网服务:来源地址、访问路径、响应状态以及管理操作记录。建议通过请求 ID 串联各层日志。示例查询逻辑如下,具体字段需按实际日志平台调整:-- 示例:在结构化日志中定位一次越权请求 SELECT ts, request_id, actor_id, tenant_id, method, path, resource_id, decision, source_ip FROM api_audit WHERE request_id = 'AUTHZ-TEST-001' ORDER BY ts ASC;重点观察普通账号访问管理员接口时是否出现明确的 deny 事件,以及应用访问内网资源时是否能关联到原始请求。若只有网关日志而没有应用侧权限判定日志,后续很难区分攻击、误操作和自动化任务。七、风险判断与修复建议现象风险判断修复建议普通用户访问管理员接口返回 200功能级授权缺失,可能泄露敏感信息在服务端统一实施 RBAC/ABAC,默认拒绝,补充接口级测试修改对象 ID 可读取他人数据对象级授权缺失,可能造成跨租户数据泄露所有查询增加资源归属和租户条件,禁止仅按 ID 查询应用服务器可访问不必要的内网端口网络边界过宽,增加横向移动风险按业务需要收敛安全组、ACL 和出站策略,分离管理网段拒绝请求没有审计记录无法及时发现和追溯越权行为记录主体、资源、策略、结果和请求 ID,避免记录完整令牌修复后应使用原测试用例进行回归:普通账号访问管理员接口必须返回 403;跨对象访问必须返回 403 或统一的 404;应用服务器对非必要内网端口无法建立连接;日志中能够看到拒绝原因和关联请求 ID。同时增加自动化授权测试,覆盖匿名用户、普通用户、同租户用户、跨租户用户和管理员五类身份。总结问答中心里遇到权限边界问题时,建议按“基线请求—最小化验证—日志关联—修复回归”的顺序处理。不要把前端隐藏按钮、单次 403 或网络不可达当成完整结论。真正有价值的报告应说明哪类身份、通过哪条请求、访问了哪类资源、经过了哪些网络边界、留下了哪些日志,以及修复后如何证明问题已经关闭。 网络请求、日志与边界流量分析示意 服务器、云资产与权限边界梳理示意
创建帐户或登录后发表意见