尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

GraphQL安全测试实战:从端点发现到攻击利用

发布时间:2026/9/16 22:11:27

资讯中心
01
ARTICLE

GraphQL安全测试实战:从端点发现到攻击利用

GraphQL安全测试实战:从端点发现到攻击利用
1. HTB 这个评估到底考什么GraphQL 安全测试的全貌搞安全测试的兄弟应该都清楚Hack The Box 的 Skills Assessment 系列不是那种随便点点就能混过去的题库它要求你在限定时间里对一个模拟目标完成从信息收集到漏洞利用的完整链路。这次碰到的Attacking GraphQL技能评估核心就是把 GraphQL 接口当成攻击面考察测试者对这种 API 查询语言的安全测试能力。先把话说清楚GraphQL 不是一个小众玩意。GitHub、Shopify、Meta、福布斯这些体量的产品都在用它允许客户端精确请求自己需要的数据省流量、省请求次数前后端协作效率高。但麻烦就麻烦在它的灵活查询天生就是双刃剑——REST 接口的端点、参数、返回结构都摆在明面上你能按图索骥找问题GraphQL 只有一个统一的端点所有的查询都靠客户端自己拼这反而给攻击者留下了远比 REST 更大的发挥空间。Introspection 泄不泄露 schema、嵌套查询能不能打穿资源限制、resolver 里有没有做权限校验这些在 REST 时代压根不用操心的问题到了 GraphQL 这里全变成了高频漏洞点。这套技能评估最值得做的地方就在这它不是让你背几个 CVE 编号就完事而是把整个测试流程拆开逼你一条条走通——找到 GraphQL 端点、枚举出 schema、分析出可利用的 query 和 mutation、构造恶意查询拿数据或绕过限制。整个过程走完一遍你对 GraphQL 安全的理解会从知道有这么回事变成真能上手干活。2. 先搞清楚你面对的是什么GraphQL 的核心机制和攻击面2.1 GraphQL 和 REST 的本质差异决定了攻击思路完全不同做测试的人如果还抱着 REST 那套思路去搞 GraphQL十有八九要碰壁。REST 里你有 GET、POST、PUT、DELETE 这些 HTTP 方法对应不同操作端点是 /users、/orders、/products 这种清晰的路由GraphQL 不一样它基本只用一个端点比如 /graphql 或 /v1/graphql所有的操作都通过 POST 发一段 query 字符串过去。这段 query 字符串长什么样我直接给个例子query { user(id: 123) { name email posts { title } } }服务端收到这段查询后会按图索骥从根节点找起先解析 user(id: 123) 这个字段再由 user 下的子字段决定要返回哪些数据。这跟 SQL 的select 指定列思路非常像只不过 GraphQL 的对象图是你自己在 query 里定义的。理解了这一点攻击面的轮廓就开始清晰了。REST 的攻击面是端点参数返回结构你逐个端点去测就完事。GraphQL 的攻击面只有一个入口但入口背后的对象关系图可能极其复杂。你得先搞到这个图才能决定打哪、怎么打。而这恰恰就是我在评估里最花时间的阶段。2.2 GraphQL 的 Schema、Query、Mutation、Resolver 到底怎么回事很多刚接触 GraphQL 的人会被一堆名词绕晕其实拆开理解很简单。Schema 就是接口的类型定义文档它声明了这个 API 里存在哪些对象、每个对象有什么字段、字段是什么类型。比如type User { id: ID! username: String! email: String password: String posts: [Post] } type Query { user(id: ID!): User users: [User] post(id: ID!): Post } type Mutation { login(username: String!, password: String!): AuthPayload updateProfile(bio: String!): User }Query 是查询入口对应 REST 里的 GET用来读数据Mutation 是变更入口对应 POST/PUT/DELETE用来写数据。至于 Resolver它是每个字段的实际处理函数数据从哪拿、权限判断在哪做全在 Resolver 里。判断一个 GraphQL 应用安不安全关键看两层暴露层和执行层。暴露层就是 Schema 本身你是否把不该暴露的字段比如 password、内部 token也写进了类型定义里执行层就是 Resolver 实现权限校验有没有严格做查询参数有没有被拼接进数据库操作。这两层只要有一处稀烂这个 GraphQL API 就基本是裸奔状态。2.3 GraphQL 攻击面全景图从枚举到利用我在评估里把攻击步骤拆成了几个阶段每个阶段对应一类问题实际操作时也是按这个顺序往下推的阶段核心动作主要风险点信息收集找 GraphQL 端点、探测是否有 introspection无 introspection 时靠字典爆破字段名Schema 枚举通过 introspection 或工具拉取完整类型定义敏感字段、内部接口暴露查询分析逐个 query/mutation 梳理参数类型和返回结构参数过滤不严、权限缺失漏洞利用构造恶意查询绕权限或打资源耗尽嵌套查询、别名批量、IDOR、注入这套流程跟 REST 测试的最大区别在第 3 和第 4 步REST 的参数你就照着一个端点慢慢测GraphQL 你得先理解对象之间的关联关系再决定如何构造一条高价值查询链。比如评估里可能出现这样的场景User 对象关联了 Account 对象Account 对象里又有 role 字段你需要通过这条链路拼出管理员账号信息。3. 踩点阶段从零开始识别 GraphQL 端点并枚举 Schema3.1 怎么快速找到 GraphQL 端点拿到靶机或目标站点的基本信息后第一步永远是找入口。GraphQL 端点不像 REST 端点那样有明显的命名规律但有迹可循。最常见的路径有/graphql、/graphiql、/v1/graphql、/api/graphql、/query、/gql。如果目标是个单页应用你可以先拉一遍前端 JS 文件在里面搜 graphql 或 apollo 或 urql 这些关键字基本能锁定请求发到哪个后端地址。这一步在 HTB 的评估里非常关键因为很多机器会故意把 GraphQL 接口藏在一个不太显眼的子路径上。找到疑似端点后怎么确认它是 GraphQL很简单向这个地址发一个 POST 请求Content-Type 用 application/jsonbody 里放一段最基础的查询比如{query: { __typename }}如果返回结果是{data: {__typename: Query}}之类的内容恭喜你找到 GraphQL 端点了。__typename 是 GraphQL 的内置元字段不需要 schema 里定义就能查属于非常实用的验货方法。另外一个加分项是看响应头。有些框架会暴露自己的指纹信息比如响应头里带有X-GraphQL-*之类的自定义头或者返回 400 错误时错误信息里直接带 GraphQL 字样。我在评估里就遇到过目标直接返回了 Apollo Server 的报错 JSON这等于对方帮你把答案写在了门上。3.2 IntrospectionGraphQL 的自带说明书怎么用确认是 GraphQL 端点后第一件事就是试 introspection。Introspection 是 GraphQL 规范里内置的自省能力你可以查询当前 API 支持哪些类型、哪些字段、哪些参数。很多没做安全加固的服务默认开着这个功能相当于把整个 API 的类型定义文档直接送给你。最基础的自省查询长这样query { __schema { types { name kind } } }这一步能拿到所有类型的名称列表但还不够细。真正干活时我一般直接跑一段完整的 introspection 查询把 schema 全量拉出来涵盖 Query、Mutation 和所有自定义类型query { __schema { queryType { name } mutationType { name } subscriptionType { name } types { name kind description fields { name description type { kind name ofType { kind name ofType { kind name } } } args { name type { kind name ofType { kind name } } } } inputFields { name type { kind name ofType { kind name } } } } } }这段东西有点长但在 Burp Suite 里我可以直接复用请求模板。HTTP 方法 POSTContent-Type 是 application/jsonbody 里把 query 字段写好就能拿到一份结构化的 schema 数据。拿到之后我会先扫一眼有没有特别扎眼的字段名比如 password、token、secret、apiKey、internal、admin 这类关键字。注意introspection 返回的数据有时候特别大一整个 schema 可能有几百 KB。我一般会把响应存成文件然后用 jq 或者简单的 Python 脚本做过滤而不是直接肉眼盯着 Burp 的输出窗口看——几十个类型上百个字段看久了眼睛真的会花。如果 introspection 被禁了也别慌不代表没有其他路。可以用工具对字段名做字典爆破或者结合前端 JS 里残留的 query 片段来猜测。不过这些都是备选方案评估里大概率不会把路堵得这么死。3.3 工具辅助InQL、GraphQL Map、graphql-cop 怎么用最顺手用手写 introspection 查询拿 schema 完全可行但效率一般。我在实际测试里一般配合几款顺手工具一起用。Burp Suite 的 InQL 插件算是首选。装好之后对着 GraphQL 请求右键就能选 InQL Scanner它会自动帮你跑 introspection把解析出来的 schema 生成一份可视化的结构树查到的 query、mutation、字段、参数一目了然。用 InQL 有个好处是它可以生成每个 query/mutation 的模板请求你直接在 Burp 的 Repeater 里改参数就能逐条测试不用自己手敲一堆 GraphQL 语法。另一个我常用的思路是用 graphql-cop 做一轮半自动检查。它是一款基于 Node.js 的开源安全扫描工具对着 GraphQL 端点跑一下会自动检查 introspection 是否开启、是否支持 alias 批处理、深度限制、CSRF 等问题。它跑出来的报告不一定覆盖所有风险点但作为前期踩点挺省时间。GraphQL Map 也值得一提它能基于 introspection 结果自动分析出可攻击的 query并尝试执行一些常见攻击载荷比如绕过认证、注入、批量查询等。工具输出的信息质量取决于你喂给它的 schema 完整度所以先确认 introspection 拿到完整数据再跑它效果会好很多。不过我还是多说一句工具能帮你扫但帮你想。评估里真正值钱的不是把工具跑一遍而是你能从 schema 里敏锐地看出来这个 mutation 看似是更新个人资料其实可以通过传参修改 role 字段。这个分析能力是任何工具替代不了的。4. 拿数据从 Schema 到实战攻击路径4.1 信息泄露Schema 里藏着多少不该出现的东西GraphQL 的 schema 设计如果不够克制很容易把内部数据结构完全暴露出来。我在评估和真实项目里见过最典型的几个问题第一个是敏感字段直接出现在类型定义里。比如 User 类型下直接定义了一个emailVerified字段还算正常但如果出现passwordHash、resetToken、internalId这就是明显的设计失误。攻击者只要在查询里加上这些字段名就能直接拿到本不该暴露的数据。而且很多 GraphQL 服务的 Resolver 是自动生成的ORM 模型里有什么字段它就暴露什么字段开发者根本没想到要手动过滤。第二个问题是过度宽松的返回结构。有些查询返回的是一个完整对象里面包含了几十上百个字段其中很多是内部标记位、时间戳、文件路径。你在评估时要养成多拉字段的习惯——有些隐藏字段不会出现在官方文档里但只要 schema 里有就能查出来。实际测试时怎么发现这些信息去翻 introspection 拿到的字段列表重点搜索这几个关键字password、credential、token、secret、key、hash、internal、debug、admin。一个字段都不要放过尤其是那些看起来很不起眼的字段。有一次我在一个评估环境里发现用户对象的notes字段能返回管理员留下的日志信息里面直接包含了 flag 文件路径。这就是字段多拉的回报。4.2 嵌套深度攻击一条查询打垮整个后端GraphQL 有个特性是对象之间可以无限互相引用比如 User 有 postsPost 有 authorauthor 又是 User理论上你能写出一层套一层的递归查询。如果服务端没有做查询深度限制这种嵌套查询能把 CPU 和数据库连接池直接打满造成拒绝服务。我构造过的最经典的嵌套攻击长这样query { user(id: 1) { posts { author { posts { author { posts { author { posts { author { name } } } } } } } } } }每一次嵌套解析都会触发一次数据库查询或对象解析呈指数级增长。如果目标服务没有深度限制比如默认只允许几层这种查询会非常消耗资源。在 HTB 的评估环境里这类攻击更多是验证防护策略是否存在而不是真的要打垮靶机。测试时我一般会先查一个中等深度的嵌套看响应时间和错误信息如果服务端返回了 maximum depth exceeded 之类的提示说明它有深度限制如果正常返回数据说明防护缺失。判断完就没有必要继续加深了毕竟授权测试也要有分寸为拿 flag 把靶机打崩反而是给自己添麻烦。4.3 别名批量攻击绕过速率限制的骚操作如果说嵌套攻击是纵向往深处打那别名批量就是横向往宽处打。GraphQL 允许在一次查询里通过 alias 别名多次调用同一个字段query { a1: user(id: 1) { name } a2: user(id: 2) { name } a3: user(id: 3) { name } a4: user(id: 4) { name } a5: user(id: 5) { name } }这在正常业务里是性能优化手段但攻击者可以把它变成暴力破解和枚举工具。比如你想枚举一批用户的手机号就一个个 id 往别名里塞想爆破登录接口就把不同的密码组合用别名塞进同一个 query 里。很多 GraphQL 服务的速率限制是针对 HTTP 请求次数的但你别名批量塞几百个操作在一个请求里速率限制直接失效。在评估里我遇到过要求枚举某个资源的场景目标有一个 query 可以根据文档 ID 获取内容但单个请求只允许查一份。我用别名批量构造了上百个 ID 的查询一次性把所有文档全拉了下来包括一个标注为内部使用的文档flag 就在里面。这里有个实操技巧手动敲这么长的 query 不现实我习惯生成并复制query { a0: getDoc(id: 1) { content } a1: getDoc(id: 2) { content } ... }建议直接用 Burp 的 Intruder 或者脚本工具生成。另外注意一下别一次性塞太多有些服务端可能对单次请求的查询复杂度或别名数量做了限制塞太多会把请求整个拒绝。我知道的是有些框架对 alias 数量超过 1000 的请求直接返回错误所以测试时可以从 100 起步逐步往上加看服务端能撑到多少。4.4 认证绕过与 IDORReslover 层能不能守住权限边界GraphQL 的认证和授权是两层东西。认证简单就是确认你是谁一般通过 JWT、Session Cookie 或 API Key 完成授权复杂是确认你能不能干这件事。很多 GraphQL 服务只做了认证没做授权结果就是一个普通用户可以查所有人的私人数据。我见过最典型的案例是这种 mutationmutation { updateUser( input: { userId: 3, role: admin, email: hackerevil.com } ) { id role } }如果 Resolver 只校验了你是否登录没有校验你要改的这个用户是不是你自己那你直接就能把自己的账号提升到管理员权限或者篡改别人的信息。这在安全测试里叫不安全的直接对象引用REST 时代有GraphQL 时代依然存在。测试这类问题时思路要广一点先去 schema 里找有没有类似user(id: Int)、updateUser(input)、deleteUser(id: ID!)这种以 ID 为参数的 query 或 mutation然后尝试用别人的 ID 去调用。不要只盯着当前登录用户的权限多想想如果换了别人的 ID结果会怎样。另一个容易忽略的点是 mutation 和 query 的分离。有些服务只对 mutation 做了 CSRF 防护或权限校验但 query 里同样有关键数据。我在评估里遇到过一种情况mutation 挂了一层管理员校验但 query 的users字段没有做任何过滤直接返回了所有用户的完整信息。而所有用户里恰好包含了管理员的 resetToken这个 token 又能用来重置管理员密码。整条攻击链就这么串起来了。5. 收尾阶段常见卡点和实操经验5.1 我在这套评估里踩过的坑和应对方案整个过程走下来有几个地方是新手最容易卡的。第一个坑是 Introspection 被禁之后不会往下一步。很多测试者一发现 introspection 被禁就觉得思路断掉了其实完全不用慌。你可以用工具做字段名词典爆破比如用 SecLists 里的 graphql 字典去测试常见字段名。另一个思路是断网分析它的报错信息有些服务在查询不存在的字段时会返回错误提示提示里会包含部分 schema 信息——比如Unknown field passwordd on type User这句话直接暴露了有个 type 叫 User。这种错误驱动的信息泄露是个很实用的小技巧。第二个坑是命中了 mutation 参数类型错误但看不懂服务端的报错。GraphQL 规范的报错里有locations、path等信息看着像天书其实都在告诉你哪里出了问题。Unknown argument说明你参数名不对Expected type Int说明类型不对Cannot query field说明字段名不存在。熟悉这些报错能帮你少走大量弯路。第三个坑是忽略 GET 请求。GraphQL 其实支持通过 GET 传 query只要把 query 拼在 URL 参数里就行。有些服务虽然禁了 POST 的 introspection但 GET 方式还开着这等于留了个后门。而且 GET 请求的 query 会出现在访问日志里如果服务端有日志注入或其他日志相关漏洞也是一个可拓展的攻击面。5.2 防御视角这套评估反推出 GraphQL 安全的真正要点做过攻击测试之后再看防御很多问题会变得一目了然。这里把我在评估中体会最深、也是真实生产环境中最容易被忽略的几个防护要点按优先级列出来生产环境必须禁用 introspection测试环境、联调环境可以开线上环境直接关。Introspection 是信息泄露的头号入口关了它攻击者至少盲人摸象得多花几倍时间。设置查询深度限制和复杂度限制深度限制建议 5 到 8 层复杂度限制要结合业务类型来定像嵌套列表字段权重就要提高。限制单次请求的别名数量主流框架如 Apollo Server 都有 alias 数量限制配置默认别嫌太少可以根据实际业务调高到 100 或 200但绝对不能无上限。授权校验放 Resolver不要放全局中间件中间件最多做认证这个用户能不能操作这份数据的校验必须落到每个 Resolver 的实现里不然 IDOR 一打一个准。响应用最小化原则GraphQL 的好处是客户端自选字段但服务端应该在返回前做一次字段过滤内部标记位、日志信息不要出现在返回对象里。还有一个很多开发者容易忽略的点不要直接把 ORM 模型暴露成 GraphQL 类型。ORM 模型里经常有password_digest、is_admin这种内部字段暴露出去等于给自己埋雷。正确的做法是定义独立的 DTO 类型只暴露业务需要的字段。但这个建议在实际项目里推行起来比较难因为图省事的开发太多了。5.3 最后一锤定音这套评估我在实际做的时候的体会最后再说点跟具体技术无关的东西。我在做 HTB 这套 GraphQL 评估时最强烈的感受是它考察的不是你会不会背攻击载荷而是你懂不懂 GraphQL 本身。你如果只背了一堆 payload 去套遇到不同 schema 直接傻眼但如果你理解了 Schema 定义了什么、Resolver 做了什么、权限校验藏在哪里那不管靶机怎么变你的思路都不会乱。建议所有准备参加这套评估的兄弟动手之前先把 GraphQL 官方的学习文档过一遍至少搞清楚 Schema、Object Type、Query、Mutation、Alias、Fragments、Directives 这几个核心概念。我在评估中已经在靶机上实际体会过多次前一个漏洞利用成功的关键不是你会不会写某个特定 payload而是你对 GraphQL 查询特性的理解能给你多少种组合尝试的可能。举个例子你如果熟悉 Fragments 的用法在遇到字段很多的对象时就能用 fragment 把公共字段抽取出来让 payload 更短更清晰排查问题也更快。你如果知道 GraphQL 的 Directives 支持条件查询就能利用include和skip做更精细的探测。这些都不是什么高深攻击技巧但真的到了实战中就是比别人快半步。整个评估做完你最大的收获其实不是我拿到 flag 了这个结果而是你脑子里终于建立起了对 GraphQL 安全的完整认知框架——从端点识别到 Schema 枚举从查询分析到漏洞利用最后再回到防御视角去理解为什么这里会出问题。这套方法论不止适用于 HTB 的评估环境放到真实授权测试项目里同样管用。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。