GraphQL安全问题

本文介绍了GraphQL的使用,它提供一种API解决方案,通过面向对象思想将数据关联,一般只提供一个接口,一次请求可获多个数据。同时阐述了GraphQL的安全风险,如GraphiQL无验证、内省机制致信息泄露、自动绑定、QL注入、Dos攻击和鉴权不严等问题,并给出部分规避方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

graphql使用

在我看来,graphql提供了一种api的解决方案。以前我们查询api提供的数据是怎么做的呢?举一个例子,例如现在有这么一个查询书籍信息的接口:/book/info,按照我们之前的解决方案,我们一般会传一个类似book_id的参数到这个接口,然后接口把书籍信息返回给我们

同样的,如果又有一个查询作者信息的需求,那么我们会新增一个接口:/author/info,并以book_id为参数查询作者信息

可以看到,以往我们使用的api查询数据的操作是分散的,彼此之间是没啥联系,查询多个数据就要向不同的接口发送多个请求

但是graphql很特别,它一般只提供一个接口/graphql,所有的数据查询操作都是向此接口发送请求,并且,只需要发送一次请求就可以获得多个数据

那graphql是怎么实现这一点的呢?

这得归功于graphql的面向对象思想,在graphql中,所有的数据形成了一张彼此相互关联的“图”,此处的“图”是指数据结构中的“图”,怕大家忘了啥是图,贴一张图回忆回忆

图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。
图论是一种表示 “多对多” 的关系
图是由顶点和边组成的:(可以无边,但至少包含一个顶点)

图中的每一个顶点就是咱们的数据,那这些数据是怎么关联起来的呢?

实际上还是借助了面向对象的思想,graphql把每一种数据都给抽象成了一个类,例如咱们上面提到的两个接口,在graphql中,这两个接口背后对应的数据就可以被抽象为Book类以及Author类

但是,这里说的类只是一个概念,区别于咱们编程语言中的类,与其说它是一个类,不如说它是一个结构,在Graphql中,Book类可以这样描述

type Book {
    id: ID
    name: String
    pageCount: Int
    author: Author
}

Author类:

type Author {
    id: ID
    firstName: String
    lastName: String
    book: Book
}

其中Book类有一个属性是author,代表一本书的作者,而Author类中有一个属性是book,表示该作者写过的所有的书,这两个类一下子就产生了联系

如果用图论中的有向图表示这两个顶点,就是下面这个样子:

在这里插入图片描述

现在咱们只是对数据做了抽象描述,那么怎么把数据暴露给前端呢?咱们还需要借助一个类型

这个类型就是Query,在Query类中我们可以定义各个查询接口:

type Query {
    bookById(id: ID): Book
    authorById(id: ID): Author
}

上面就在Query类中定义了两个接口,bookById(id: ID): Book表示对外暴露的接口名为bookById,然后这个接口接受一个参数id,这个id的类型为ID,该接口的返回类型为Book

暴露了接口,我们前端就可以查询了,前端查询的格式也是很讲究的,例如我要查id为book-1的书籍的名字和页数,就可以传如下数据到接口/graphql:

{
    bookByID(id: "book-1"){
        name,
        pageCount
    }
}

如果我要查,id为book-1的书籍的名字以及其作者的名字,可以发送如下格式的数据:

{
    bookByID(id: "book-1"){
        name,
        author{
            firstName,
            lastName
        }
    }
}

返回结果也是很清晰的,如下:

总结起来,graphql的工作流程如下:

当然,这也只是一个整体上的流程,没有涉及到具体的代码实现,其实在写代码的时候还得考虑数据来自哪里,这就涉及到为每个数据类型配置dataFetcher了,具体的代码实现案例可以看官网的入门教程(java版),不再赘述:

https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/#author-datafetcher

graphql安全风险

graphiql对外网开放,且无验证机制

graphiql实际上就是一个graphql的调试工具,有了该调试工具可以更方便我们执行graphql语句,但是对攻击者来说如果一个程序对外提供了/graphql接口,开没开graphiql接口也就没那么重要了

因为graphiql接口能做的,graphql接口也可以做到🤣

graphql内省机制

内省官方文档:https://graphql.cn/learn/introspection/

graphql内省机制涉及到的最大的问题就是信息泄露了,如果系统没有对内省机制进行处理,则可能会泄露系统中所有的可用的查询,以及查询支持的字段等等,也就是说整个系统处于裸奔状态

如果系统中有一些敏感查询,则会泄露很多信息,甚至影响正常的业务逻辑

graphql支持的内省查询有两个__schema、__type,其实使用__schema就够用了

查询graphql中注册的所有类型:

可以看到查询到了我们之前注册的Book以及Author类型,但是想要进一步利用,只是知道系统中注册的所有类型还不行,我们需要知道系统支持哪些查询,用如下语句:

在这里插入图片描述

可以看到,我们之前注册的两个查询也出来了,但是还不够,咱们还得知道这些查询下支持哪些字段以及是否需要参数:

在这里插入图片描述

可以看到成功查询出了bookById的查询的参数是id,返回类型是Book,然后我们在去看一下Book的字段就知道bookByID可以查询哪些字段了:

可以看到,Book下的字段有id,name,pageCount,author,那么我们就可以直接构造查询了:

上述过程就是一个完整的graphql内省机制的利用流程,我这里演示的接口就是单纯的查询信息,没啥危害,但是要知道真实环境中的graphql api可不只是用来做查询操作的,还有各种写操作,造成的危害也就上了一个层次

规避方案:
目前还不了解graphql是否支持关闭内省机制,但是我们可以在业务上对内省机制进行限制,例如过滤查询中的__schema、__type关键词,这两个关键词都是小写,所以暂时也不需要担心大写绕过的问题(但是业务上对大小写进行了不正确的转换就另说了)

自动绑定

自动绑定原理其实很简单,和spring中的自动绑定机制有点类似,就是如果我爆破出了你的敏感接口、以及对应的字段,我就可以直接使用,这本就是graphql的一个特点,不算是漏洞,但是这确实会带来一些安全问题

规避方案:

  • 对敏感的graphql查询进行权限控制

ql注入

graphql实现了一套自己的查询语言,和spring中的el类似,graphql也是存在表达式注入的问题

漏洞原理:
前端传入的参数未正确处理就直接拼接到了graphql语句中,例如下面这样的场景

String book_id = req.getParameter("book_id");
String gql_query = "{bookById(id:\""+book_id+"\"){name,pageCount}}";
用gql_query执行查询

现在如果我给book_id赋值为book-1"){name}authorById("author-1"){firstName}#

gql_query的值就变为:{bookById(id:"book-1"){name}authorById("author-1"){firstName}#"){name,pageCount}}

其中,#是注释符,经过拼接,查询的语义完全变了

当然,这样使用graphql的场景应该也不多,毕竟这已经违背了graphql的设计理念…

Dos攻击

其实我上面给的案例的那种写法就存在着Dos隐患,问题出在两个类型互相关联上,Book中有Author,Author中有Book,当数据量够大时,这样我们可以直接构造多层查询达到Dos效果

类似于xee攻击:https://www.cnblogs.com/lcamry/p/5737318.html

鉴权不严

鉴权问题是api通用的问题,只是开发者在使用graphql的过程中更容易忽略对敏感接口进行细粒度的鉴权,因为graphql是单路由的api,所以,开发者往往也只是对这个路由进行了权限判断,但是实际上graphql中注册的各个查询可能要求的权限是不一样的

例如只有admin权限才能调用的查询addMoney,就需要在业务上限制非admin用户不能访问

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值