Strix GraphQL 安全测试技能解析:从攻击面测绘、授权绕过到联邦利用的完整实战手册
本文以 Strix 仓库中的技能文件 graphql 为主体,完整展开这份面向 AI 渗透测试代理的 GraphQL API 安全测试手册:如何发现 GraphQL 端点与获取 schema、如何用别名与批量请求探测字段级 IDOR、如何攻击 Relay 节点、游标、指令、联邦(_service/_entities)与订阅,以及配套的 WAF 绕过、绕过技巧、七步测试方法论和漏洞验证要求。读完后你既能把这套方法直接用于人工渗透,也能理解 Strix 如何把该技能动态注入代理系统提示词、让 Agent 自动执行这一整套流程。
一、技能文件的定位:它如何成为 Strix 代理的知识
graphql 是 Strix protocols(协议类)技能目录下的一个技能文件,文件头部使用 YAML frontmatter 声明元数据:
---
name: graphql
description: GraphQL security testing covering introspection, resolver injection, batching attacks, and authorization bypass
---
从源码结构看,Strix 的技能系统由三部分协作完成"文档 → 代理知识"的转化:
- 元数据解析与加载:strix/skills/__init__.py 中的
_FRONTMATTER_PATTERN正则匹配---包裹的 frontmatter,_parse_skill_content用yaml.safe_load解析元数据并剥离出 markdown 正文;load_skills按名称在技能目录(内置strix/skills及通过register_skill_dir注册的自定义目录)中定位<category>/<name>.md文件,读取正文返回。 - 系统提示词注入:strix/agents/prompt.py 的
render_system_prompt调用_resolve_skills按顺序组装技能列表——先放调用方请求的技能,再强制追加scan_modes/<mode>、tooling/agent_browser、tooling/python、analysis/counterevidence、analysis/severity_calibration(以及根代理专属的coordination/root_agent),去重后通过load_skills载入并渲染进system_prompt.jinja模板。也就是说,graphql技能一旦指定,其全文会进入该代理的系统提示词,使代理"变成"具备深度 GraphQL 测试经验的专家。 - 运行时按需加载:strix/agents/factory.py 将
load_skill纳入每个代理的基础工具集_BASE_TOOLS;该工具(strix/tools/load_skill/tool.py)允许代理在执行到未预载技术时即时把技能正文作为工具结果载入对话。validate_requested_skills强制限制每次最多 5 个技能,并要求在重名时使用category/name限定名。
strix/skills/README.md 也说明了代理创建时的典型用法(每个代理最多加载 5 个技能,动态注入系统提示词)。下文主体内容即该技能文件的技术正文,按原文件脉络完整展开。
二、攻击面:GraphQL API 的五个维度
技能开篇即界定测试范围:聚焦 resolver 级授权、字段/边访问控制、批量请求滥用与联邦信任边界。攻击面划分为四个维度:
操作(Operations)
- Queries、mutations、subscriptions
- 持久化查询 / 自动持久化查询(APQ)
传输(Transports)
- HTTP POST/GET,
application/json或application/graphql - WebSocket:
graphql-ws、graphql-transport-ws协议 - 文件上传用的 Multipart
Schema 特性(Schema Features)
- 内省(Introspection):
__schema、__type - 指令:
@defer、@stream、自定义授权指令(@auth、@private) - 自定义标量:
Upload、JSON、DateTime - Relay:全局 node ID、连接/游标、接口/联合类型
架构(Architecture)
- 联邦(Apollo、GraphQL Mesh):
_service、_entities - 网关与子图之间的授权边界差异
三、侦察:端点发现、Schema 获取与类型图谱
端点发现
对常见路径批量发送最小探测请求 {"query":"{__typename}"}:
POST /graphql {"query":"{__typename}"}
POST /api/graphql {"query":"{__typename}"}
POST /v1/graphql {"query":"{__typename}"}
POST /gql {"query":"{__typename}"}
GET /graphql?query={__typename}
__typename 请求无需任何权限即可返回,若响应含 data: { __typename: "Query" } 类字段即确认是 GraphQL 端点。同时检查 GraphiQL/Playground 是否暴露且允许携带凭证——跨域且带 cookie 的 GraphiQL 可通过 postMessage 桥接泄露数据。
Schema 获取
内省开启时,直接用内省查询拉取类型骨架:
{__schema{types{name fields{name args{name}}}}}
内省禁用时,通过五类信号推断 schema:
- 对候选字段发
__typename探测; - 字段建议错误:提交"近似名"(near-miss)字段名,从错误的 suggestions 中收集真实字段;
- "Expected one of" 错误会暴露枚举(enum)取值;
- 类型强转错误暴露字段结构;
- 错误分类学:区分"未知字段"与"未授权字段"的不同错误码,本身就能揭示字段是否存在。
Schema 映射
将根操作、对象类型、接口/联合、指令、自定义标量画成类型图谱,识别敏感字段:email、token、roles、billing、API key、admin 标志、文件 URL。特别标注级联路径——子 resolver 可能基于父对象已校验的假设而跳过自身鉴权,这是后续授权测试的重点。
四、核心漏洞:授权绕过
字段级 IDOR
利用别名在同一请求中对比自有对象与他人的对象:
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 } # Child may skip auth check
}
}
这类"级联假设"是 GraphQL 最典型的越权来源:只要父对象能解析,子字段就可能无鉴权暴露。
Relay 节点解析
Relay 的全局 ID 通常为 base64 编码的 Type:id。解码后交换 type/id 组合:
query {
node(id:"VXNlcjoxMjM=") { ... on User { email } }
}
要点:确认每个类型的授权在各自 resolver 内强制执行;验证连接的过滤条件(owner/tenant)在分页之前生效;篡改游标不应跨越所有权边界。
Mutation 绕过
- 探测部分更新是否绕过校验(JSON Merge Patch 语义下,未传字段可能不触发完整校验链);
- 测试接受额外字段并透传给下游逻辑的 mutation(参数注入)。
五、核心漏洞:批量请求与别名滥用
别名枚举
query {
u1:user(id:"1"){email}
u2:user(id:"2"){email}
u3:user(id:"3"){email}
}
一次请求携带 N 个对象引用,直接绕过"每请求一次"的限速,并暴露"按字段鉴权"与"按请求鉴权"之间的不一致——很多中间件只检查请求级 token,不检查每个别名解析出的对象是否属于该主体。
数组批量(Array Batching)
GraphQL 规范不要求支持数组 body,但不少实现放行。若服务端支持,提交多个操作可同时获得部分失败语义并进一步突破限额。
六、核心漏洞:输入操纵
类型混淆
同一字段提交不同形态,观察解析/校验差异:
{id: 123} vs {id: "123"}
{id: [123]} vs {id: null}
{id: 0} vs {id: -1}
数字与字符串、数组、null、边界值的差异可能命中不同 resolver 分支或绕过前置校验。
重复键
{"id": 1, "id": 2}
JSON 解析器对重复键的取值优先级(前者胜/后者胜)因库而异,可能导致校验看到的值与实际传值不一致,绕过校验;同时应测试默认参数值是否按预期生效。
额外字段
在输入对象里塞入预期之外的键;部分后端会把整个 input 透传给 resolver 或下游 ORM(等价于 mass assignment 面)。
七、核心漏洞:游标操纵
分页游标通常为 base64,解码后可:
- 篡改偏移/ID 跳过数据;
- 跳过过滤条件;
- 跨越所有权边界(用 A 用户的游标结构访问 B 的页)。
八、核心漏洞:指令滥用
@defer / @stream
query {
me { id }
... @defer { adminPanel { secrets } }
}
增量投递(incremental delivery)下,受限数据可能在后续 payload 中返回,而部分授权逻辑只在首次响应前执行。前提:确认服务端确实支持增量投递。
自定义指令
@auth、@private 等指令往往只是"标注意图",并不真正强制执行——必须验证每条 resolver 路径上是否存在实际检查,而非相信 schema 上的注释语义。
九、核心漏洞:复杂度攻击
片段炸弹(Fragment Bomb)
fragment x on User { friends { ...x } }
query { me { ...x } }
递归展开产生指数级复杂度,用于压测深度限制、复杂度限制、查询成本分析器与超时配置。
宽选择集(Wide Selection Sets)
利用选择集与片段强制过度获取(overfetching)敏感子字段,尤其针对默认展开深层嵌套的场景。
十、核心漏洞:联邦(Federation)利用
SDL 暴露
query { _service { sdl } }
子图的完整 SDL 会暴露内部类型、字段名与关联关系,为后续跨子图越权提供地图。
实体物化
query {
_entities(representations:[
{__typename:"User", id:"TARGET_ID"}
]) { ... on User { email roles } }
}
网关(Gateway)可能执行了鉴权,但子图(Subgraph)的 resolver 未必——重点寻找跨子图 IDOR:不同子图对同一实体的 ownership 校验不一致,即可物化出本不该可见的数据。
十一、核心漏洞:订阅、持久化查询、CORS/CSRF 与文件上传
订阅(Subscription)安全
- 授权只在握手时做,未按消息校验;
- 通过过滤参数订阅他人频道;
- 跨租户事件泄露;
- 利用订阅 resolver 的 filter 参数引用他人 ID。
持久化查询滥用
- 从前端 bundle 中泄露的 APQ hash 直接重放;
- 用攻击者自己的 variables 重放高权限操作;
- 对常见操作做 hash 爆破;
- 验证 hash→operation 映射是否强制执行主体绑定与操作白名单。
CORS & CSRF
- Cookie 认证 + GET 查询时,mutation 可能通过 query 参数触发 CSRF;
- 跨域带凭证的 GraphiQL/Playground 泄露数据;
- 缺失 SameSite 与 origin 校验。
文件上传(GraphQL multipart 规范)
- 多个
Upload标量组合; - 文件名/路径穿越技巧;
- 意外 content-type、超大 chunk;
- 返回 URL 的服务端所有权/作用域控制。
十二、WAF 规避与传输层绕过
技能将"防护绕过"单列两节,核心思路是不改变语义的前提下让 WAF 签名失配:
查询重塑(Query Reshaping)
- 注释与块字符串(
"""...""")拆分 token; - Unicode 转义;
- 别名/片段间接引用;
- JSON variables 与内联参数互换;
- GET / POST /
application/graphql三种入口互换。
片段拆分(Fragment Splitting)
把敏感字段拆到不同 fragment 与内联展开中,规避朴素签名匹配:
fragment a on User { email }
fragment b on User { password }
query { me { ...a ...b } }
传输切换(Transport Switching)
同一 payload 分别经四种入口投递,观察授权/限流一致性:
Content-Type: application/json
Content-Type: application/graphql
Content-Type: multipart/form-data
GET with query params
时序与限速
- HTTP/2 多路复用与连接复用拉宽时间窗;
- 批量请求绕限速。
命名技巧
- 大小写/下划线变体;
- Unicode 同形字符(依赖服务端实现);
- 用别名掩盖敏感字段名。
缓存混淆
- CDN 缓存未带
Vary: Authorization; - 变量操纵影响缓存键;
- 重定向与 304/206 行为泄露部分响应体。
十三、测试方法论(七步流程)
技能给出的标准化作业顺序:
- Fingerprint — 识别端点、传输方式、技术栈(Apollo、Hasura 等)、GraphiQL 暴露情况;
- Schema mapping — 通过内省或推断构建完整类型图谱;
- Principal matrix — 收集未认证、普通用户、付费、管理员等多角色 token,且每个主体至少配一个有效对象 ID;
- Field sweep — 用别名在同一请求中以"自有 ID vs 他人 ID"扫描每个 resolver;
- Transport parity — 验证 HTTP、WebSocket、持久化查询各传输的授权一致性;
- Federation probe — 对
_service与_entities测试子图授权缺口; - Edge cases — 游标、
@defer/@stream、订阅、文件上传。
十四、验证要求:什么才算有效发现
技能末尾明确了结论的证据标准,这与 Strix 全技能库统一的"防误报"纪律一致(根代理还会被强制加载 strix/skills/analysis/counterevidence.md 与 strix/skills/analysis/severity_calibration.md):
- 成对请求(owner vs non-owner)证明未授权访问;
- Resolver 级绕过:父级有检查、子字段暴露数据;
- 传输一致性证据:同一操作在 HTTP 与 WebSocket 下表现不同;
- 联邦绕过:
_entities在无子图鉴权时取到数据; - 最小 payload,给出精确的选择集与变量形态;
- 记录漏掉强制检查的精确 resolver 路径。
十五、源码级实现:技能如何被验证与装载
结合仓库源码,可以确认上述技能在 Strix 中的完整生命周期:
- 加载与校验:strix/skills/__init__.py 中
validate_requested_skills限制单代理最多 5 个技能、拒绝未知名称、对重名技能要求category/name限定;load_skills按"自定义目录优先、内置目录兜底"的顺序解析文件,剥去 frontmatter 后返回正文,并对内置技能名做加载追踪(_track_skill_loaded)。 - 提示词装配:strix/agents/prompt.py 的
_resolve_skills保证任何代理都携带扫描模式、浏览器/Python 工具 playbook 与"反证/严重度校准"两类分析技能;render_system_prompt通过 Jinja2 的get_skill全局函数把技能正文渲染进系统提示词,graphql技能因此成为对应代理的常驻知识。 - 代理构建:strix/agents/factory.py 的
build_strix_agent(skills=[...], ...)接收技能列表并传给提示词渲染;子代理经make_child_factory继承扫描级配置。这意味着当 Strix 根代理发现目标是 GraphQL API 时,可以派生一个携带skills=["graphql"](可叠加idor、ssrf等)的专家子代理执行第二至十四节的整套流程。 - 动态载入:会话中代理还可调用 load_skill 工具即时拉取技能正文,无需重建代理;技能名称匹配
strix/skills/<category>/<name>.md的文件名。
参考文件
| 文件 | 作用 |
|---|---|
| strix/skills/protocols/graphql.md | 本文主体的 GraphQL 测试技能(攻击面、漏洞、绕过、方法论、验证要求) |
| strix/skills/protocols/oauth.md | 同目录的 OAuth/OIDC 技能,可对照阅读协议类技能写法 |
| strix/skills/__init__.py | 技能发现、frontmatter 解析、校验与加载 |
| strix/tools/load_skill/tool.py | 代理运行时按需加载技能的 load_skill 工具 |
| strix/agents/prompt.py | 技能顺序解析与系统提示词渲染 |
| strix/agents/factory.py | 代理构建、技能注入与基础工具集装配 |
| strix/skills/README.md | 技能体系总览与分类说明 |
| docs/advanced/skills.mdx | 官方文档中的技能机制说明 |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00