在2022New Star碰到GraphQL相关题目(ezAPI),记录一下GraphQL的学习。

GraphQL简介

GraphQL是由Facebook开发并于2015年公开发布的数据查询语言。是REST API的替代品。作为一种 API 查询语言,GraphQL 不要求实现它的应用服务器使用特定的编程语言或存储系统,且它与底层数据库无关,可以与MySQL数据库一起使用,也可以与Nosql数据库搭配。

与其类似的还有RESTful API,区别在于,RESTful API需要多个API实现不同的功能,而GraphQL获取多个资源,只用一个请求。但是也不是所有需求都适用GraphQL,简单的需求还是使用RESTful API,GraphQL查询的数据是图状数据结构,一些没有图的概念的数据使用GraphQL并没有显著的效果。

SQL是结构化查询语言,GraphQL是图表化查询语言。

GraphQL查询响应格式

基本的GraphQL查询

1
2
3
4
5
6
7
8
query{
user{
​ id
​ email
​ firstName
​ lastName
}
}

基本的GraphQL响应

响应结果则是json类型:

1
2
3
4
5
6
7
8
9
10
{
"data": {
"user": {
"id": "666",
"email": "xxx@qq.com",
"firstName": "xx",
"lastName": "xx"
}
}
}

官方文档:

1
2
查询和其结果拥有几乎一样的结构。这是 GraphQL 最重要的特性,
因为这样一来,你就总是能得到你想要的数据,而服务器也准确地知道客户端请求的字段。

例如这样的

image-20221004200939127

类似于json,但是有很多换行符。

GraphQL主要操作

字段

GraphQL 关于请求对象上的特定字段,即要操作(查询更改…)的对象。

参数

GraphQL 对象类型上的每一个字段都可能有零个或者多个参数。

GraphQL基本参数类型

  • String, Int, Float, Boolean和ID。在schema声明时直接使用。 (ID不能重复使用)
  • !(叹号)代表参数不能为空。

基础操作

类似于SQL的增删改查,GraphQL主要有三种操作query、mutation 和 subscription,分别对应查询、变更和订阅。

官方文档:

1
2
3
操作类型可以是 query、mutation 或 subscription,
描述你打算做什么类型的操作。操作类型是必需的,
除非你使用查询简写语法,在这种情况下,你无法为操作提供名称或变量定义。

内省查询

GraphQL内置了内省系统,通过它给出的内省方法,我们可以获得对象名称、类型、参数等多种信息。

官方文档:

1
2
3
4
5
6
7
8
9
10
我们也可以通过查询 __schema 字段来向 GraphQL 询问哪些类型是可用的。
一个查询的根类型总是有 __schema 这个字段。

有好多类型!它们都是什么?我们来总结一下:
Query, Character, Human, Episode, Droid
- 这些是我们在类型系统中定义的类型。
String, Boolean
- 这些是内建的标量,由类型系统提供。
__Schema, __Type, __TypeKind, __Field, __InputValue, __EnumValue, __Directive
- 这些有着两个下划线的类型是内省系统的一部分。

我的理解:information_schema(像不像 (*^▽^*)

重点

上述查询是可交互的。也就是你可以按你喜欢来改变查询,然后看看新的结果。(也就是可以更改查询语句得到想要的结果)

GraphQL 对象类型上的每一个字段都可能有零个或者多个参数。(参数个数不同可能得不到我们想要的内容,比如flag)

例子

1
data={"query":"query{\nusers_user_by_pk(id:1){\nname\n}\n}\n","variables":null}
1
2
3
4
5
6
7
8
{"query":
"query{
users_user_by_pk(id:1)
{
name
}
}
","variables":null}

从users_user_by_pk这个接口查询id=1的字段的name属性。

GraphQL 端点

常见的GraphQL端点:

1
2
3
4
5
6
/graphql/
/graphql/console/
/graphql.php
/graphiql/
/graphiql.php
[...]

可以通过向端点发送一个无效查询,是否报错判断端点。

在测试应用程序时,验证是否可以在没有一般授权令牌标头的情况下发出请求,因为GraphQL本身没有提供任何数据保护手段,只能通过其他方法进行访问控制,不让端点被恶意利用。

GraphQL可能存在的隐患

未授权访问

开发者在使用时经常会忽略接口的鉴权问题。导致访问控制容易被破坏,不安全的对象直接引用甚至SQL或NoSQL进行注入。有时候客户端调用查询接口,直接传入了id等信息并未做好权限校验,就有可能存在水平越权。

GraphQL内省查询

最常见的问题,内省查询本来应该是仅允许内部访问,但配置错误导致任何攻击者可以利用内省查询。

payload:

1
{"query":"{__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}}}}}}}}","variables":{}}

返回包返回的就是该API端点的所有信息。

通过内省查询得到很多接口信息,造成信息泄露。

嵌套查询DoS

当业务的变量互相关联,如以下graphql定义为这样时,就可能无限展开,造成拒绝服务。

它们可以允许恶意客户端通过过度复杂的查询来执行DoS(拒绝服务)攻击,占用服务器的资源。

1
2
3
4
5
6
7
8
9
type Thread {
messages(first: Int, after: String): [Message]
}
type Message {
thread: Thread
}
type Query {
thread(id: ID!): Thread
}

image-20221004232945314

针对DoS的简单修复方式可以是设置超时,最大深度或查询复杂度阈值。

SQL注入

graphql的sql注入与一般的sql注入类似,都是可以通过构造恶意语句达到注入获取数据或改变查询逻辑的目的。p神在先知大会上讲过该类问题,借用p神的2张PPT。

image-20221004233248293

例题

参考2022NewStarCTF ezAPI。

参考文章

https://blog.doyensec.com/2018/05/17/graphql-security-overview.html

https://www.anquanke.com/post/id/147455#h2-9

https://www.secpulse.com/archives/148242.html

https://zhuanlan.zhihu.com/p/124019191#

https://www.zhihu.com/question/264629587

https://zhuanlan.zhihu.com/p/506391425

https://blog.csdn.net/A_bad_horse/article/details/105377150

官方文档:

https://graphql.bootcss.com/