GraphQL 是什么?
GraphQL 是一种用于 API(应用程序编程接口)的查询语言,以及一个用于执行这些查询的服务器端运行时系统。它由 Facebook 于 2012 年开发并于 2015 年开源。GraphQL 允许客户端仅请求所需的数据,并从服务器接收结构化的响应,从而提高了数据传输的效率和灵活性。GraphQL 可以类比 SQL(Structured Query Language),它们都是一种查询语言,通过查询语句可以获得期望的结果。不同之处有两点:
-
SQL 是基于结构化的数据模型,而 GraphQL 基于图。 -
SQL 是从数据库查询,而 GraphQL 是从服务器查询。
以下是 GraphQL 的一些主要特点:
-
客户端指定数据需求:客户端可以指定需要哪些数据字段,从而避免了不必要的数据传输。 -
单一请求获取多资源:客户端可以在一个请求中获取多个资源的数据,而无需发起多个 HTTP 请求。 -
强类型系统:GraphQL 使用一种强类型系统,允许开发者定义 API 的类型结构。这使得开发者可以在开发阶段捕获许多错误,并生成文档和工具。 -
实时更新:通过订阅(subscriptions),客户端可以实时接收来自服务器的数据更新。 -
与 REST 的对比:与传统的 REST API 相比,GraphQL 提供了更灵活的数据查询方式,减少了过多或不足的数据传输问题。
一个简单的 GraphQL 查询示例如下:
query {
posts {
id
content
}
}
这个查询会返回所有帖子的信息。GraphQL 的服务器端通常用一些常见的编程语言和框架实现,比如 Node.js、Python、Java、Ruby 等等。客户端可以使用各种库和工具来发起 GraphQL 查询,比如 Apollo Client、Relay 等。
GraphQL 环境搭建
项目使用的技术栈:
-
Typescript:JavaScript 的类型化超集,可编译为纯 JavaScript。 -
Node.js:一个基于 Chrome V8 引擎的 JavaScript 运行时。 -
GraphQL:API 的查询语言,用于使用现有数据来完成这些查询的运行时。 -
TypeGraphQL:Node.js 中 GraphQL API 的现代框架。
-
项目初始化:
mkdir graphql && cd graphql
npm init -y
2.安装依赖包:
npm install [email protected]
[email protected]
[email protected]
[email protected]
[email protected]
npm install --save-dev @types/[email protected]
@types/[email protected]
[email protected]
[email protected]
在项目根目录加入文件 graphql/tsconfig.json:
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"lib": ["es2018", "esnext.asynciterable"],
"strictFunctionTypes": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
tsconfig.json 是 Typescript 的配置文件,它指定编译项目所需的根文件和编译器选项。准备工作已经做完了,接下来我们写点代码。创建 graphql/src/index.ts 文件,并写入相关代码:
修改package.json内容:
{
"name": "graphql",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "ts-node src"
}
..........
}
运行npm start
结果如下:
接下来我们引入GraphQL,开始写一些逻辑代码:修改 src/index.ts为:
import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";
@ObjectType()
class Post {
@Field(type => ID)
id: string;
@Field()
created: Date;
@Field()
content: string;
}
@Resolver(Post)
class PostResolver {
@Query(returns => [Post])
async posts(): Promise<Post[]> {
return [
{
id: "0",
created: new Date(),
content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...'
},
{
id: "1",
created: new Date(),
content: '为什么GraphQL是API的未来'
},
{
id: "2",
created: new Date(),
content: 'Netflix:我们为什么要将 GraphQL 引入前端架构?'
},
]
}
}
async function main() {
try {
const schema = await buildSchema({
resolvers: [PostResolver],
dateScalarMode: 'timestamp'
});
const server = new ApolloServer({
schema,
playground: true
});
const { url } = await server.listen(4444);
console.log(`GraphQL Playground available at ${url}`);
} catch (error) {
console.error(error);
}
}
main();
再次运行项目:npm start
访问:http://localhost:4444/我们先点击右侧 SCHEMA 按钮,在左则输入查询语句
{
posts {
id
content
}
}
返回所有id和content内容
GraphQL基础
1. Mutation
Mutation 是 GraphQL 中用于数据修改的操作,可以用于创建、更新或删除数据。例如,更新用户信息、创建新帖子或删除现有评论。以下是一个简单的 Mutation,用于更新指定 ID 的帖子内容:
mutation {
updatePostContent(id: "1", content: "这是更新后的内容") {
id
created
content
}
}
我们通过查询后发现内容被修改了:
2. 自省查询
{
__schema {
types {
name
}
}
}
这个查询会返回 GraphQL API 中所有类型的名称。
GraphQL API 漏洞测试
1. 查找graphQL路径
以下是一些常见的GraphQL路径:
/graphql
/graphiql
/v1/graphql
/v2/graphql
/v3/graphql
/v1/graphiql
/v2/graphiql
/v3/graphiql
/api/graphql
/api/graphiql
/graphql/api
/graphql/console
/console
/playground
/gql
/query
/graphql-devtools
/graphql-explorer
/graphql-playground
/graphql.php
/index.php?graphql
......
2. 敏感信息查找
自省是一种让客户端查询 GraphQL 服务自身结构的机制。这使得客户端可以动态地了解服务支持的查询和变更类型。自省查询是普通 GraphQL 查询的一部分,但它们专门用于获取 GraphQL 服务的元数据。GraphQL 通过利用内省系统,可列出 GraphQL中所有Query、Mutation、ObjectType、Field、Arguments。查询存在的类型:
query {__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
在测试过程中,我们可以根据获取到的接口去构造query查询,以便寻找敏感信息,如email,password等 。
3. 越权修改数据
也可以构造mutation语句去越权修改数据等
mutation {
updatePassword(id: "1", newPassword: "newSecretPassword123") {
id
email
firstName
lastName
password
}
}
4. 查找管理员密码
我们通过graphql接口,查询找到管理员密码,登入后台删除carlos 用户完成实验。实验地址:https://portswigger.net/web-security/graphql/lab-graphql-accidental-field-exposure
首先通过自省查询获取GraphQL架构的详细信息。它请求了架构中所有类型的信息,包括每种类型的字段及其参数的详细信息,构如下语句:
{"operationName":"IntrospectionQuery","variables":{},"query":"query IntrospectionQuery {__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}"}
我可以从返回的信息中发现user这个类型中存在username和password字段,可以通过构造getUser去查询user的数据
构造如下查询语句:
{
"operationName": "getUser",
"variables": {
"id": 1
},
"query": "query getUser($id: Int!) { getUser(id: $id) { id username password } }"
}
执行成功返回管理员密码登录后台,删除carlos用户即可完成试验。
5. GraphQL SSRF请求伪造攻击
在DVGA主页上,单击导入粘贴。页面有一个接受 URL 的字段,如果在GraphQL相关查询操作允许本地主机或其他服务器不限制输入,就可能遭受服务端请求伪造攻击。
6. 任意文件写入
在GraphQL API 中,文件上传、文件下载等操作也有可能任意文件写入、任意文件读取、目录穿越等漏洞。首先在一个文件上传点抓包,发现存在GraphQL 接口
修改filename文件上传路径
文件被上传至tmp目录
7.GraphQL安全测试工具
GraphQLmap是一个可以跟GraphQL节点交互的脚本引擎,广大研究人员可以使用GraphQLmap来针对 GraphQL节点进行渗透测试和安全研究。工具安装:项目主页: https://github.com/swisskyrepo/GraphQLmap连接一个graphql节点命令如下
./bin/graphqlmap -u http://192.168.231.1:4444/graphql
连接graphql节点成功后,使用dump_via_fragment 命令查询graphql架构信息,Query、Mutation、等信息。
总结
本文通过详细介绍 GraphQL 的基本概念、环境搭建、基础操作和安全测试,帮助读者理解并应用这一现代 API 查询语言。通过构建一个简单的 GraphQL 项目,展示了从初始化到实现查询和变更的全过程。文章重点讨论了 GraphQL API 的安全性,列举了多种常见的安全测试方法,包括路径查找、敏感信息查找、越权操作、SSRF 攻击和任意文件写入等。最后,介绍了实用的安全测试工具 GraphQLmap,提供了具体的使用方法。希望本文能为开发者提供有价值的参考,帮助他们在实际项目中更好地利用和保护 GraphQL API。
原文始发于微信公众号(山石网科安全技术研究院):浅谈GraphQL API安全测试