GraphQL API漏洞笔记
个人总结
参考:https://portswigger.net/web-security/graphql
- name: 个人总结
desc: 参考:https://portswigger.net/web-security/graphql
bgColor: '#F0DFB1'
textColor: 'green'
2
3
4
# GraphQL API漏洞笔记
官方 GraphQL 学习教程 (opens new window)
官方 GraphQL 学习教程 (opens new window)
官方 GraphQL 学习教程 (opens new window)
# 1GraphQL基本知识
# 1、什么是GraphQL?
答案
- GraphQL 是一种 API 查询语言,旨在促进客户端和服务器之间的高效通信。
- 用户不需要知道数据的存储位置,他只需要指出想要的数据字段,然后将查询发送到 GraphQL 服务器,让服务器帮他获取数据并返回给他即可。
- 在 REST API 中,GraphQL 有助于避免出现大型响应对象和重复调用。
- GraphQL 与平台无关,因此它可以使用多种编程语言实现,并且可以与几乎任何数据存储进行通信。
GraphQL(API查询语言)的概念有点像 SQL(数据库查询语言),都是用于检索某种数据的查询语言。
-- 这是SQL
SELECT id, name, description FROM product WHERE id=1
2
# 这是GraphQL
query {
product(id: 1) {
id
name
description
}
}
2
3
4
5
6
7
8
怎样,这俩是不是很像?
# 2、什么是GraphQL模式?它定义了什么?
答案
在 GraphQL 中,模式(schema)用于表示服务器前端和后端之间的协议。GraphQL 使用人类可读的语言来定义模式,并将可用的数据定义为一系列类型(各种数据类型)。然后,服务可以根据这些类型生成出具体的数据。
GraphQL 模式定义了服务数据的结构、可以列出可用的对象(类型)、字段和关系。
# 3、GraphQL有哪几种数据处理操作?
答案
三种:查询、变更和订阅
- 查询并提取数据。
- 变更可以被添加、更改或删除的数据。
- 订阅类似于查询,但它设置了一个持久性连接,服务器通过该连接,可以将指定格式的数据 主动推送到客户端。
# 4、REST API和GraphQL的不同点?
答案
REST API
- 多个端点,不同的端点用于区分不同的操作
- 需要使用多种 HTTP 请求方法来进行不同的操作,例如一个
/api/data
端点,你通过GET
请求访问之后是获取数据,但通过PUT
请求访问就变成了上传数据 - 响应的数据格式不固定,由服务器人员自定义
GraphQL
- 单个端点,所有操作集成到一个端点上,GraphQL 会自动根据你发送的 类型/名称 来判断你具体要做哪一个操作
- 通常使用
POST
请求来发送数据 - 响应的数据格式一般为 JSON
# 2GraphQL查询和变更
# 1、什么是GraphQL查询?查询由哪几个关键部分组成?
答案
GraphQL 查询从数据存储中检索数据。它们大致等同于 REST API 中的 GET 请求。
查询由四个部分组成:
- 操作类型。告诉服务器 当前请求需要进行哪些操作。
- 查询名称。就是一个名称,有点像函数名,有助于调试。
- 数据结构。你想要查询 并 让服务器返回的数据。
- (可选)一个或多个参数。这些参数用于创建查询 并 返回特定对象的详细信息(例如,“为我提供 ID 为 123 的产品名称和描述”)。
这是一个示例查询:
- 操作类型为
query
(查询),告诉服务器我要查询数据 - 查询名称为
myGetProductQuery
,是自定义的名称,我取名叫 “abcdefg” 也可以,最好是一个方便记忆的名称 - 数据结构为
getProduct {name description}
,这里还包含一个参数id: 123
,意思是获取getProduct
对象中id
为123
的两个字段数据
#Example query
query myGetProductQuery {
getProduct(id: 123) {
name
description
}
}
2
3
4
5
6
7
8
# 2、什么是GraphQL变更?变更由哪几个关键部分组成?
答案
GraphQL 变更以某种方式改变数据,包括添加、删除或编辑数据。它们大致等同于 REST API 的 POST、PUT 和 DELETE 方法。
变更的组成部分和 查询 一样,操作类型 + 操作名称 + 数据结构 + 可选参数。
这是一个示例变更请求:
- 操作类型为
mutation
(变更),告诉服务器我要修改数据 - 该请求省略了操作名称
- 数据结构为
createProduct {id name listed}
- 提供了两个参数
name: "Flamin' Cocktail Glasses", listed: "yes"
#示例变更请求
mutation {
createProduct(name: "Flamin' Cocktail Glasses", listed: "yes") {
id
name
listed
}
}
2
3
4
5
6
7
8
9
10
这是由以上请求产生的响应:
#示例变更响应
{
"data": {
"createProduct": {
"id": 123,
"name": "Flamin' Cocktail Glasses",
"listed": "yes"
}
}
}
2
3
4
5
6
7
8
9
10
11
# 3、查询和变更由哪几个组成部分?
点击查看
五个:字段、参数、变量、别名 和 片段。
以下是一个示例请求(字段 + 参数 + 别名):
- 位于对象中的
id
、name.firstname
和name.lastname
是请求字段 - 位于括号中的
id: 1
和id: 2
是参数 product1
和product2
是别名,单个请求中不能出现同一个对象 “getProduct”,这会冲突,所以要设置别名
query myGetEmployeeQuery {
product1: getProduct(id: 1) {
id
name {
firstname
lastname
}
}
product2: getProduct(id: 2) {
id
name {
firstname
lastname
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
以下是第二个示例请求(字段 + 参数 + 变量 + 片段):
- 位于
Variables
中的id
是一个变量,它被定义为1
$id: ID!
声明了一个变量,符号!
表示该参数是必填字段,id: $id
是一个使用了以上变量的动态参数productInfo
是一个片段,它包含Product
中的多个字段。你可以通过...productInfo
来使用它。- 片段是由多个字段组成的
query myGetEmployeeQuery($id: ID!) {
getProduct(id: $id) {
...productInfo
name {
firstname
lastname
}
}
}
Variables: {
"id": 1
}
fragment productInfo on Product {
id
name
listed
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3GraphQL订阅和内省
# 1、GraphQL订阅是什么?
答案
订阅是一种特殊类型的查询。它使客户端能够与服务器建立长期连接,这样服务器就可以向客户端推送实时更新,而不需要一直轮询数据。订阅主要用于对大型对象的微小更改,以及需要小型实时更新的功能(如聊天系统或协作编辑)。
- 与常规查询和变更一样,订阅请求定义了要返回的数据结构。
- 订阅通常使用 WebSocket 实现。
# 2、GraphQL内省是什么?
答案
(其实就是查询 GraphQL 自身的环境信息)
- “内省” 是一个内置的 GraphQL 函数,可用于查询服务器以获取有关模式的信息。它通常被用于 GraphQL IDE 和文档生成工具等应用程序。
- 与常规查询一样,你可以指定要返回的响应字段和结构。例如,你可能希望响应仅包含可用于变更的名称。
# 3、GraphQL内省可能导致什么危害?
答案
内省可能具有严重的信息泄露风险,因为它可用于访问潜在的敏感信息(例如字段描述)并帮助攻击者了解如何与 API 交互。
建议在生产环境中禁用 GraphQL 内省,这是最佳实践。
# 4GraphQL API漏洞
# 1、挖掘GraphQL API漏洞的流程是?
答案
- 查找 GraphQL API 端点
- 在发现的端点中寻找漏洞
- 测试查询参数,如果 API 直接通过参数来访问对象,则可能存在 IDOR 漏洞
- 测试别名,如果该端点存在速率限制,尝试使用别名绕过速率限制
- 通过内省探测模式信息,尝试泄露潜在的敏感信息
- (如果有的话)绕过各种防御
- 识别有缺陷的正则表达式,绕过 GraphQL 内省防御
# 2、如何查找GraphQL API端点?
答案
(GraphQL 服务通常会响应任何非 GraphQL 请求,并返回 “查询不存在” 等类似的错误。在测试 GraphQL 端点时,你应该牢记这一点)以下是一些查找和测试端点的方法:
- 通用查询。将查询
query{__typename}
发送至任意 URL,如果响应中返回了{"data": {"__typename": "query"}}
,则此处可能是 GraphQL 端点 - 通用端点名称。例如
/graphql
、/api
、/api/graphql
、/graphql/api
、/graphql/graphql
、/api/v1/graphql
......等 - 使用不同的 HTTP 方法和内容类型。GraphQL 端点一般只允许内容类型为
application/json
的POST
请求,但某些端点可能会接受替代方法,例如内容类型为x-www-form-urlencoded
的GET
或POST
请求 - 初始检查。通过 BurpSuite 捕获请求,检查数据包中是否有任何 GraphQL 查询
- 使用 Burp Scanner。通过 Burp Scanner 主动扫描目标站点,如果发现了任何 GraphQL 端点,则 BurpSuite 会报告 “GraphQL endpoint found” 问题
# 3、如何探测GraphQL API的模式信息?
答案
流程:
- 使用和探测内省。使用以下查询
{"query": "{__schema{queryType{name}}}"}
来探测内省 - 运行完整的内省查询。这部分内容太长了,要回去看文章
- 可视化内省结果。可以使用在线工具 (opens new window)或 BurpSuite 扩展 InQL (opens new window) 来完成
- 探测 “建议”。如果内省被禁用,则可以尝试通过 “建议” 来探测 API 模式信息。诸如 Clairvoyance (opens new window) 之类的工具可以完成自动化收集,以减轻工作量
- 使用 Burp Scanner。通过 Burp Scanner 主动扫描目标站点,如果发现了任何可用的 “内省”,则 BurpSuite 会报告 “GraphQL introspection enabled” 问题。如果发现了任何可用的 “建议”,则 BurpSuite 会报告 “GraphQL suggestions enabled” 问题
# 4、如何绕过GraphQL内省防御?
答案
- 添加混淆字符。在查询中添加空格、换行符、逗号等字符,GraphQL 会忽略这些字符,但有缺陷的正则表达式不会忽略它们
- 篡改内容类型。尝试修改内容类型为
x-www-form-urlencoded
的 POST 请求 - 使用其他 HTTP 请求方法。例如,尝试通过 GET 请求来发送查询:
GET /graphql?query=query%7B__schema%0A%7BqueryType%7Bname%7D%7D%7D
# 5、如何使用别名绕过速率限制?
答案
别名的使用格式为<别名>:<对象名>(){...}
,你应该看得懂:
#使用别名查询的请求
query QueryUser($id: Int) {
isUser(id:$id){
valid
}
user2:isUser(id:$id){
valid
}
user3:isUser(id:$id){
valid
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 6、GraphQL中的CSRF漏洞是如何产生?怎么利用?
答案
如果 GraphQL 没有实现任何 CSRF 令牌,且内容类型没有限定为application/json
,则 GraphQL 可以被作为 CSRF 攻击的载体。
GraphQL+CSRF 漏洞利用的构造方式,与常规的 CSRF 攻击一样(都是通过 GET/POST 请求发送数据,当然一样了)。
# 5GraphQL API漏洞防御
# 1、防范常规的GraphQL攻击
答案
- 对于内部 GraphQL API 服务,应该禁用内省,降低不必要的信息泄露风险。
- 如果 GraphQL API 服务提供给公众使用,你可能需要启用内省,此时应该检查 API 模式信息,以确保它不会向大众公开非预期的字段。
- 确保你的 API 模式信息,不会公开任何私有用户字段,例如电子邮件地址或用户 ID。
- 确保 “建议” 已被禁用。防止攻击者使用 Clairvoyance 或类似的工具来收集有关底层模式的信息。解决方案请参阅此 GitHub 项目 (opens new window)。
(你可能会疑惑,为什么内部要禁用,外部却启用内省?)
- 因为内部使用的 GraphQL 可能存有敏感信息,所以要禁用内省,以防止内部敏感信息被泄露
- 外部使用的 GraphQL 都是开放的公共接口,保存的一般都是公开信息,开放内省是为了让用户了解 API 结构,以更好地与接口进行数据交互
# 2、防范GraphQL暴力攻击/拒绝服务攻击
针对两个漏洞进行防御:
- 通过别名绕过速率限制
- 通过 GraphQL 实现的 DoS 攻击
答案
- 限制 API 查询的深度(查询的嵌套层级数)。大量的查询可能会对性能造成影响,产生 DoS 攻击。
- 限制 API 所能接收 字段/别名 的最大数量。例如,攻击者发送一千个带有别名的字段,进行暴力攻击。
- 限制查询的最大字节数。
- 实现成本分析。识别一个 查询任务 所需要消耗的资源成本,如果查询过于复杂而无法运行,则丢弃该查询任务。
# 3、防范GraphQL中的CSRF攻击
答案
- 只接受内容类型为 JSON 数据的 POST 请求
- 验证内容类型 与 实际接收的数据是否吻合
- 在 GraphQL 中实现 CSRF 令牌机制