GraphQL介绍
GraphQL概述
GraphQL与传统RESTful API的区别
GraphQL 允许客户端在一个请求中明确地指定需要的数据,并返回预期的结果,而不是像 RESTful API 那样只能获取预定义的资源。因此,GraphQL 简化了客户端与服务器之间的通信,提高了应用程序的性能和可扩展性。
GraphQL应用
目前正在使用GraphQL的大型公司包括Facebook、PayPal、GitHub、Shopify、Twitter、Tesla、Hackerone等大型公司,可见GraphQL正在迅猛发展。
GraphQL渗透测试前置知识
GraphQL查询语言
- Query
- Mutation
- Subscription
- Input
- Enum
- Union
- Interface
我们接下来主要介绍一下Query、Mutation、Subscription这三种。
Query
例如,以下查询会请求服务器返回用户ID为1的用户名和电子邮件地址:
query {
user(id: 1 {
name
email
}
}
Mutation
Mutation用于在服务端修改或添加数据,类似于RESTful API的POST、PUT和DELETE方法。Mutation支持向服务端提交一些参数,并根据参数来执行相应的操作。
mutation {
updateUser(id: 1, name: "NewName" {
id
name
email
}
}
Subscription
Subscription是GraphQL中的一种高级特性,它允许客户端通过WebSocket连接实时接收来自服务器的数据更新。这对于需要实时通知的应用程序非常有用,如在线聊天、股票报价等。
subscription {
newMessage(channel: "chat" {
content
author
}
}
GraphQL渗透测试方法和流程
- 渗透测试准备工作
- 渗透测试步骤
- 枚举 GraphQL Schema
- 查询漏洞
- 注入漏洞
- 认证和授权漏洞
常见的GraphQL路径
/graphql
/graphql-console
/graphql-devtools
/graphql-explorer
/graphql-playground
/graphql-playground-html
/graphql.php
/graphql/console
/graphql/graphql
/graphql/graphql-playground
/graphql/schema.json
/graphql/schema.xml
/graphql/schema.yaml
/graphql/v1
/HyperGraphQL
/je/graphql
/laravel-graphql-playground
/lol/graphql
/portal-graphql
/v1/api/graphql
/v1/graphql
/v1/graphql-explorer
/v1/graphql.php
/v1/graphql/console
/v1/graphql/schema.json
/v1/graphql/schema.xml
/v1/graphql/schema.yaml
/v2/api/graphql
/v2/graphql
/v2/graphql-explorer
/v2/graphql.php
/graph
/graphql/console/
/graphiql
/graphiql.php
内省攻击
内省攻击本质上来说就是因为没有修改默认配置导致攻击者可以通过查询 __schema
或者 __type
等系统级别的 Schema 来获取服务器上定义的所有类型、字段、枚举等信息。攻击者可以利用这些信息去了解服务器的数据结构和业务逻辑,为下一步的攻击行动做准备。
- 获取所有暴露的接口和类型信息:攻击者可以使用
- 获取所有字段信息:攻击者可以通过
__type.fields
查询来获取所有字段信息,包括字段名称、类型和描述等。 - 获取实际数据:攻击者可以通过查询实际数据,如用户账号、密码、秘密答案等,从而获取更高的权限或者直接篡改系统。
__schema
查询来获取所有可用的 Schema 信息,包括对象类型、标量类型、枚举类型、接口类型和输入类型等。
如下三段GraphQL 查询语句本质上是相同的,都是用于获取 GraphQL Schema 的元数据信息。它们的区别在于格式和编写方式
{__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}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 ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}
{"query": "query IntrospectionQuery{__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}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 ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}"}
{"query":"query Query {\n __schema {\n queryType { name }\n mutationType { name }\n subscriptionType { name }\n types {\n ...FullType\n }\n directives {\n name\n description\n locations\n args {\n ...InputValue\n }\n }\n }\n }\n\n fragment FullType on __Type {\n kind\n name\n description\n fields(includeDeprecated: true {\n name\n description\n args {\n ...InputValue\n }\n type {\n ...TypeRef\n }\n isDeprecated\n deprecationReason\n }\n inputFields {\n ...InputValue\n }\n interfaces {\n ...TypeRef\n }\n enumValues(includeDeprecated: true {\n name\n description\n isDeprecated\n deprecationReason\n }\n possibleTypes {\n ...TypeRef\n }\n }\n\n fragment InputValue on __InputValue {\n name\n description\n type { ...TypeRef }\n defaultValue\n }\n\n fragment TypeRef on __Type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n }"}
https://github.com/nikitastupin/clairvoyance
GraphQL 渗透测试实战案例分析
消耗服务器资源攻击案例
我们首先请求一次该接口发现用时30481
SSRF漏洞案例
防御和修复 GraphQL 漏洞的建议
- 限制查询语句的深度和复杂度:GraphQL 支持嵌套查询和参数化查询,这使得攻击者可以构造非常复杂的查询来增加服务器的负载,甚至可能导致拒绝服务攻击。为了防范此类攻击,可以对查询语句进行深度和复杂度限制,以确保每个查询不会占用过多的资源。
- 限制查询的访问权限:GraphQL API 应该只允许授权访问,而不是任何人都可以访问。可以通过身份验证、IP 白名单、API 密钥等方式来限制查询的访问权限,从而避免恶意查询和攻击。
- 限制查询返回的字段和数据量:攻击者可以利用查询返回大量的数据来耗尽服务器资源或者获取敏感信息。为了防止这种情况发生,应该限制每个查询返回的字段和数据量,并对某些敏感字段进行隐藏或者过滤处理。
- 检查输入参数的合法性:GraphQL 允许客户端自由传递参数,这也使得攻击者可以利用参数注入漏洞来攻击系统。因此,在服务器端需要对输入参数进行严格校验和过滤,避免恶意参数的注入和攻击。
- 关闭内省查询:GraphQL 内省机制被用来获取服务器上定义的所有类型、字段、枚举等信息。攻击者可以利用这些信息了解服务器的数据结构和业务逻辑,从而进行进一步攻击。为了防范此类攻击,应该关闭或限制内省查询功能。
参考
https://zhuanlan.zhihu.com/p/390876937
注:技巧,可以了解 GraphQL 模式的功能。{"query":"query {\n system\n}","variables":null}