第 7 章 · 03 协议专项(GraphQL / OAuth 2.0 / OIDC)


文档摘要

第 7 章 · 03 协议专项(GraphQL / OAuth 2.0 / OIDC) 本节摘要:协议层的攻击面与应用框架不同——它由协议规范本身的特性与实现方的常见误解决定。GraphQL 的 resolver 级鉴权常在父字段检查、子字段假设已校验,留下「边/子 resolver 缺口」;alias batching 能绕过每请求限流做枚举;federation 的 让网关与子图的鉴权边界不一致。OAuth 2.0/OIDC 的失败几乎总通向账户接管:redirect URI 操纵、authorization code 泄露/重放、state/nonce 缺失、PKCE 降级、token audience 混淆。

第 7 章 · 03 协议专项(GraphQL / OAuth 2.0 / OIDC)

本节摘要:协议层的攻击面与应用框架不同——它由协议规范本身的特性与实现方的常见误解决定。GraphQL 的 resolver 级鉴权常在父字段检查、子字段假设已校验,留下「边/子 resolver 缺口」;alias batching 能绕过每请求限流做枚举;federation 的 _entities 让网关与子图的鉴权边界不一致。OAuth 2.0/OIDC 的失败几乎总通向账户接管:redirect URI 操纵、authorization code 泄露/重放、state/nonce 缺失、PKCE 降级、token audience 混淆。本节合并这两个协议,聚焦「协议特有的攻击面与实现陷阱」,让你理解为什么「每个 redirect、每个 client identifier、每个 token 交换都是授权边界,而非便利层」。

⚠️ 仅限授权测试:本节所有技术仅用于你自己的应用或有书面授权的渗透测试。OAuth/GraphQL 的账户接管测试尤其要控制影响,避免干扰真实用户。未经授权对他人系统使用这些技术是非法的。

学习目标

阅读完本节,你应当能够:

  1. 掌握 GraphQL 的端点发现、schema 获取(introspection 与推断)与敏感字段映射。
  2. 识别 GraphQL 的 resolver 级鉴权缺口(字段级 IDOR、边/子 resolver 假设、Relay node 解析)。
  3. 利用 GraphQL 的 batching/alias 滥用、输入操纵、游标操纵、directive 滥用、复杂度攻击。
  4. 理解 federation 的信任边界(_service.sdl_entities 跨子图 IDOR)。
  5. 掌握 OAuth 2.0/OIDC 的 redirect URI 操纵(开放重定向链、校验绕过)。
  6. 识别 authorization code 泄露/重放/注入、state/nonce 缺陷、PKCE 绕过、token audience/scope 混淆。
  7. 在两个协议上做「主体矩阵 × 传输矩阵」系统化测试,并区分真正可利用与误报。

一、GraphQL

GraphQL 安全测试聚焦 resolver 级鉴权、字段/边访问控制、batching 滥用、federation 信任边界。与 REST 不同,GraphQL 把「查询哪些字段」交给客户端,这放大了鉴权漂移的影响。

1.1 攻击面

操作:query、mutation、subscription;persisted query / Automatic Persisted Query(APQ)。

传输:HTTP POST/GET(application/jsonapplication/graphql);WebSocket:graphql-ws、graphql-transport-ws 协议;multipart 文件上传。

schema 特性:introspection(__schema__type);directive(@defer@stream、自定义 auth directive @auth/@private);自定义标量(Upload、JSON、DateTime);Relay:global node ID、connection/cursor、interface/union。

架构:federation(Apollo、GraphQL Mesh):_service_entities;网关 vs 子图鉴权边界。

1.2 侦察:端点发现与 schema 获取

端点发现:

POST /graphql {"query":"{__typename}"} POST /api/graphql {"query":"{__typename}"} POST /v1/graphql {"query":"{__typename}"} POST /gql {"query":"{__typename}"} GET /graphql?query={__typename}

检查 GraphiQL/Playground 是否带凭证暴露(跨源带 cookie 可经 postMessage 桥接泄露数据)。

schema 获取:introspection 开启时:

{__schema{types{name fields{name args{name}}}}}

introspection 关闭时,经以下推断:__typename 探候选字段;字段建议错误(提交近似名收割建议);"Expected one of" 错误暴露枚举值;类型强制错误暴露字段结构;错误分类:「未知字段」vs「未授权字段」的不同错误码暴露存在性。

schema 映射:映射根操作、对象类型、interface/union、directive、自定义标量。识别敏感字段:email、token、role、billing、API key、admin 标志、文件 URL。注意子 resolver 在父假设下可能跳过鉴权的级联路径。

1.3 典型缺陷:鉴权绕过

字段级 IDOR:用 alias 在单请求对比归属 vs 外国对象:

query { own: order(id:"OWNED_ID") { id total owner { email } } foreign: order(id:"FOREIGN_ID") { id total owner { email } } }

边/子 resolver 缺口:父 resolver 查鉴权,子 resolver 假设已校验:

query { user(id:"FOREIGN") { id privateData { secrets } # 子可能跳过鉴权 } }

Relay node 解析:解码 base64 global ID,交换 type/id 对:

query { node(id:"VXNlcjoxMjM=") { ... on User { email } } }

确保每类型的鉴权在 resolver 内强制。验证 connection 过滤(归属者/租户)在分页前应用;游标篡改不应跨归属边界。

Mutation 绕过:探 mutation 的部分更新绕过校验(JSON Merge Patch 语义);测 mutation 接受额外字段传给下游逻辑。

1.4 Batching 与 Alias 滥用、输入操纵

经 alias 枚举(绕过每请求限流,暴露逐字段 vs 每请求鉴权不一致):

query { u1:user(id:"1"){email} u2:user(id:"2"){email} u3:user(id:"3"){email} }

数组 batching(若支持,非标准):提交多操作实现部分失败、绕过限制。

类型混淆:{id: 123} vs {id: "123"}{id: [123]} vs {id: null}{id: 0} vs {id: -1}

重复 key: {"id": 1, "id": 2}——parser 优先级不同,可能绕过校验。也测默认参数值。

额外字段:input object 发意外 key,后端可能传给 resolver 或下游逻辑。

1.5 游标、directive、复杂度、federation

游标操纵:解码游标(通常 base64)操纵 offset/ID、跳过过滤、跨归属边界。

directive 滥用:

@defer/@stream:

query { me { id } ... @defer { adminPanel { secrets } } }

可能在增量交付返回受门控数据。确认服务端支持增量交付。

自定义 directive:@auth@private 等常只标注意图不强制——验证每个 resolver 路径的实际检查。

复杂度攻击:

Fragment 炸弹:

fragment x on User { friends { ...x } } query { me { ...x } }

测深度/复杂度限制、查询成本分析器、超时。

宽选择集:滥用选择集与 fragment 强制过度获取敏感子字段。

federation 利用:

SDL 暴露:

query { _service { sdl } }

entity 物化:

query { _entities(representations:[ {__typename:"User", id:"TARGET_ID"} ]) { ... on User { email roles } } }

网关可能强制鉴权;子图 resolver 可能不强制。找跨子图 IDOR(归属检查不一致)。

1.6 subscription、persisted query、CORS/CSRF、文件上传

subscription 安全:只在握手鉴权、不逐消息;经 filter 参数订阅他人 channel;跨租户事件泄露;滥用 subscription resolver 的 filter 参数引用外国 ID。

persisted query 滥用:客户端 bundle 泄露的 APQ 哈希;用攻击者变量重放特权操作;常见操作的哈希爆破;验证 hash→操作映射强制 principal 与操作白名单。

CORS/CSRF:cookie 鉴权 + GET 查询启用 mutation 经查询参数的 CSRF;GraphiQL/Playground 跨源带凭证泄露数据;缺 SameSite 与 origin 校验。

文件上传(GraphQL multipart spec):多个 Upload 标量;文件名/路径穿越技巧;意外 content-type、超大 chunk;返回 URL 的服务端归属/scoping。

1.7 WAF 规避与绕过

查询重塑:注释与块字符串("""...""");Unicode 转义;alias/fragment 间接;JSON 变量 vs 内联参数;GET vs POST vs application/graphql

fragment 拆分:把字段拆进 fragment 与 inline spread 避开朴素签名:

fragment a on User { email } fragment b on User { password } query { me { ...a ...b } }

传输切换:

Content-Type: application/json Content-Type: application/graphql Content-Type: multipart/form-data GET with query params

时序与限流:HTTP/2 多路复用与连接复用拓宽时序窗;batching 绕限流。

命名技巧:大小写/下划线变体;Unicode 同形字(服务端依赖);alias 掩盖敏感字段名。

缓存混淆:CDN 缓存缺 Vary on Authorization;变量操纵影响缓存 key;重定向与 304/206 行为泄露部分响应。

1.8 测试方法与验证

  1. 指纹:识别端点、传输、栈(Apollo、Hasura 等)、GraphiQL 暴露。
  2. schema 映射:introspection 或推断构建完整类型图。
  3. 主体矩阵:收集未认证、user、premium、admin 角色的 token,每主体至少一个有效对象 ID。
  4. 字段扫描:经 alias 在单请求对每 resolver 测归属 vs 外国 ID。
  5. 传输对等:验证 HTTP、WebSocket、persisted query 的相同鉴权。
  6. federation 探针:测 _service_entities 的子图鉴权缺口。
  7. 边界:游标、@defer/@stream、subscription、文件上传。

验证:配对请求(归属者 vs 非归属者)展示非授权访问;resolver 级绕过(父检查在、子字段暴露数据);传输对等证明(HTTP 与 WebSocket 同操作);federation 绕过(_entities 无子图鉴权访问数据);最小 payload 带精确选择集与变量形态;记录漏强制的精确 resolver 路径。

💡 Pro Tips:GraphQL 的「父字段鉴权 ≠ 子字段鉴权」——user(id){privateData{secrets}}privateData resolver 常假设父已校验;alias batching 是被低估的枚举利器,既绕限流又暴露逐字段鉴权不一致;introspection 关了别放弃,__typename + 字段建议错误一样能还原 schema;federation 的 _entities 是网关-子图鉴权边界的破绽。

二、OAuth 2.0 / OIDC

OAuth 与 OIDC 的失败常通向账户接管、token 窃取、跨客户端 token 混淆。把每个 redirect、每个 client identifier、每个 token 交换都当授权边界——而非便利层。

2.1 攻击面

流程:授权码(带/不带 PKCE);implicit(legacy)、hybrid、设备授权、client credentials;refresh token 轮换、token introspection、revocation。

端点:/authorize/token/userinfo/introspect/revoke/logout;/.well-known/openid-configuration/jwks.json;动态客户端注册(若启用)。

token 类型:authorization code、access token、refresh token、ID token;opaque vs JWT 格式;reference token vs 自包含 JWT。

客户端类型:public client(SPA、移动)vs confidential(服务端);多 redirect URI、通配/模式匹配、自定义 URI scheme。

2.2 侦察

Discovery:

GET /.well-known/openid-configuration GET /oauth2/.well-known/openid-configuration GET /.well-known/oauth-authorization-server

提取:authorization_endpointtoken_endpointregistration_endpoint、支持的 response_typescode_challenge_methods_supportedgrant_types_supported

客户端枚举:检查 JS bundle、移动 APK/IPA 配置、GitHub 仓库找 client_id、redirect URI、scope;查错误消息的客户端校验提示("invalid redirect_uri"、"unregistered client")。

2.3 典型缺陷:redirect URI 操纵

开放重定向链:注册或猜宽松 redirect 模式:https://app.com/callback、仅路径前缀、子域通配;测:追加路径、fragment、查询注入、@ 技巧、编码斜杠、反斜杠变体。

https://app.com/callback.evil.com https://app.com/callback%2f..%2f@evil.com https://app.com/callback?next=https://evil.com com.app://callback (移动自定义 scheme)

redirect URI 校验绕过:尾斜杠、大小写、端口、scheme 降级(http vs https);IdP 校验器与消费应用间的路径规范化差异;redirect_uri 参数污染(首 vs 末胜);通配子域接受:*.app.com → 注册 attacker.app.com 或找悬空子域。

2.4 Authorization Code 问题

code 泄露:code 在 URL fragment、Referer 头、浏览器历史、服务端日志、分析工具;过期前重放、缺一次性使用强制;绑定弱时 code 发到错误 redirect_uri。

code 注入/mix-up:攻击者发起流程,受害者完成登录,code 送到攻击者 redirect;mix-up 攻击:在 authorize 与 token 步骤间交换 client_id;token 端点缺 redirect_uri 绑定。

2.5 state、nonce、PKCE

state/nonce:缺失、可预测或可复用的 state → OAuth 登录 CSRF(会话固定、账户链接);OIDC 缺 nonce → ID token 注入/重放;state 不绑定客户端会话或 PKCE verifier。

PKCE 绕过:code_challenge_method 降级(接受 plain 而非 S256);public client 缺 PKCE 要求;code_verifier 不校验或大小写不敏感弱匹配;授权码无 challenge 签发、token 端点接受任意 verifier。

2.6 客户端认证、scope、token、OIDC 特有

public client 滥用:token 端点对 confidential client 接受无 client_secret 请求;token/introspection 端点仅 client_id 认证;动态注册用攻击者控制的 redirect URI。

secret 泄露:移动应用、SPA、公开仓库里的硬编码 secret;查询串里的 client_secret 或记入访问日志。

scope 与 token:scope 升级——请求 admin/offline_access/openid profile email 超出应用需要,服务端授予全部请求 scope;refresh token 不轮换或复用未检测 → 持久访问;access token 跨服务接受(缺 audience/resource 绑定);token introspection 无正确鉴权返回 active:true

OIDC 特有:ID token 被资源服务器当 access token(token 混淆);step-up 要求不验 acramrauth_time;userinfo 端点无匹配 access token scope 返回 PII;不验 iss 时跨 issuer 的 sub 碰撞。

2.7 高级技巧、测试方法与验证

Referer 泄露:把授权 redirect 作为攻击者页面的子资源嵌入;策略允许时从 Referer 收割 code

设备流滥用:用猜测的 code 轮询 device_code 端点(限流缓慢);用户批准攻击者发起的设备登录。

账户链接:OAuth 登录把攻击者 IdP 身份链到受害者本地账户,无需重新认证;邮箱碰撞:不同 IdP provider 的同邮箱。

测试方法:

  1. 映射流程:识别所有 grant type、client、redirect URI。
  2. redirect 矩阵:对每 client 用编码与 parser 技巧 fuzz redirect_uri 校验。
  3. CSRF:无 state 发起 OAuth;流程中交换会话。
  4. PKCE:用错误/缺失 verifier 重放 code;降级 challenge 方法。
  5. token 交换:跨 client 交换 code/token;测跨 audience 接受。
  6. 移动/deep link:自定义 scheme、intent filter、universal link 劫持。

验证:演示经 redirect 操纵或 Referer 泄露窃取的 authorization code/token;展示用攻击者 OAuth 会话的账户接管或受害者资源访问;证明 CSRF(受害者在适用时无同意 UI 绕过完成登录进攻击者链接会话);记录精确校验缺口(redirect 绑定、PKCE、state、audience);提供完整 authorize → callback → token 请求链的前后证据。

2.8 误报、影响与工具

误报:redirect URI 在所有绕过尝试中一致拒绝;public client 正确要求 PKCE S256 与严格 verifier 校验;state/nonce 强制且绑定,CSRF 测试如预期失败;资源服务器正确校验 token audience/issuer;自定义 scheme redirect 需应用所有权证明(已验证 Android/iOS app link)。

影响:经窃取 authorization code/token 的完整账户接管;经 refresh token 窃取的持久访问;经 token 混淆的跨租户/跨客户端数据访问;userinfo 或 ID token claim 泄露的 PII 暴露。

工具:沙箱自带 jwt_tool(已克隆在 /home/pentester/tools/jwt_tool)加 curl——足以覆盖 OAuth/OIDC 的 token 侧。

python3 /home/pentester/tools/jwt_tool/jwt_tool.py <ID_TOKEN> # 解码/检查 python3 /home/pentester/tools/jwt_tool/jwt_tool.py <ID_TOKEN> -X a # alg:none python3 /home/pentester/tools/jwt_tool/jwt_tool.py <ID_TOKEN> -X k -pk pub.pem # RS256->HS256 混淆

curl 手动驱动 authorize → callback → token 链,让你控制每个参数(redirect_uriclient_idstate、PKCE code_challenge/code_verifier),测上面的绑定/降级。人类常用 Burp 的 EsPReSSO(RUB-NDS)SSO 扩展做流程可视化,但它是 GUI,沙箱内优先手动 curl + jwt_tool

💡 Pro Tips:永远捕获完整重定向链含中间 302 location;对比 authorize 步与 token 步的参数绑定(redirect_uriclient_id、PKCE);web 与移动客户端都测——校验规则常不同;检查 logout/revocation——"登出"后 token 可能仍有效;把 OAuth 链到合法 redirect_uri 上的开放重定向或 XSS 来外泄 code。

本节要点回顾

  1. GraphQL resolver 级鉴权:父字段检查 ≠ 子字段检查,user(id){privateData{secrets}}privateData 常假设父已校验——这是头号缺口。
  2. GraphQL schema 获取:introspection 开了直接 dump;关了用 __typename + 字段建议错误 + 错误分类推断,别放弃。
  3. GraphQL batching/alias:单请求多 alias 既绕每请求限流做枚举,又暴露逐字段 vs 每请求鉴权不一致;数组 batching(若支持)实现部分失败绕过限制。
  4. GraphQL federation:_service { sdl } 暴露 schema;_entities(representations:[...]) 找跨子图 IDOR,网关强制鉴权但子图可能不强制。
  5. GraphQL 复杂度/directive:fragment 炸弹(fragment x on User { friends { ...x } })测深度限制;@defer/@stream 可在增量交付返回受门控数据;@auth directive 常只标注不强制。
  6. GraphQL WAF 规避:查询重塑(注释/块字符串/Unicode 转义)、fragment 拆分、传输切换(application/json/application/graphql/multipart/GET)避开朴素签名。
  7. OAuth redirect URI:开放重定向链(callback.evil.com、编码穿越、@ 技巧)与校验绕过(尾斜杠/大小写/端口/scheme 降级/参数污染)是账户接管主路径。
  8. OAuth code/state/PKCE:code 在 Referer/日志/历史泄露;mix-up 攻击交换 client_id;缺/可预测 state = 登录 CSRF;PKCE 降级 plain 或 verifier 弱匹配。
  9. OAuth token 混淆:ID token 当 access token;跨服务 access token(缺 audience 绑定);scope 升级;refresh token 不轮换 = 持久访问;introspection 端点无鉴权。
  10. 共性心法:协议层的安全 hinges on 严格的 redirect 绑定、不可猜的 state/nonce、public client 的 PKCE、一致的 token audience 校验——authorize-to-token 链条里任何缺口都是潜在账户接管。

下一节进入基础设施层——AWS、GCP、Kubernetes。IAM 过宽、公开存储桶、元数据服务(IMDS)、RBAC、容器逃逸,是云环境的特有攻击面。


发布者: 作者: 灏天文库 转发
评论区 (0)
U