ivandotv/graphql-no-alias

GitHub: ivandotv/graphql-no-alias

GraphQL 服务端验证库,通过限制 alias 字段重复调用次数来防止批量查询攻击和 DoS。

Stars: 12 | Forks: 3

# GraphQL No Alias Directive 验证 [![测试](https://static.pigsec.cn/wp-content/uploads/repos/cas/ba/babb5334241d8d8db5d9626d913733c8bb9b24d09b622e29e0cf84b6913fb0be.svg)](https://github.com/ivandotv/graphql-no-alias/actions/workflows/CI.yml) [![Codecov](https://img.shields.io/codecov/c/gh/ivandotv/graphql-no-alias)](https://app.codecov.io/gh/ivandotv/graphql-no-alias) [![GitHub 许可证](https://img.shields.io/github/license/ivandotv/graphql-no-alias)](https://github.com/ivandotv/graphql-no-alias/blob/main/LICENSE) - [安装](#instalation) - [用法](#usage) - [使用 directive](#using-the-directive) - [Schema 设置](#schema-setup) - [Object type 用法](#object-type-usage) - [Field type 用法](#field-type-usage) - [自定义声明](#customizing-the-declaration) - [命令式配置](#imperative-configuration) - [自定义错误消息](#customizing-the-error-message) - [Envelop 插件](#envelop-plugin) - [No Batched Queries 库](#no-batched-queries-library) - [许可证](#license) Graphql 验证以及附带的 directive,用于限制可以发送到 GraphQL 服务器的 `alias` 查询和 mutation 的数量。 它将阻止类似于以下形式的某些攻击。 ``` // batch query attack (hello DoS) query { getUsers(first: 1000) second: getUsers(first: 2000) third: getUsers(first: 3000) fourth: getUsers(first: 4000) } // or batch login attack mutation { login(pass: 1111, username: "ivan") second: login(pass: 2222, username: "ivan") third: login(pass: 3333, username: "ivan") fourth: login(pass: 4444, username: "ivan") } ` ``` 你可以在这里阅读更多关于批量攻击的内容:https://lab.wallarm.com/graphql-batching-attack/ ## 安装 ``` npm i graphql-no-alias ``` ## 用法 有两种方式可以使用此验证: - 在 `schema` 中使用 `directive` - [使用配置选项](#Imperative-configuration)(性能更好) ### 使用 directive 包含两个部分:需要添加到 `schema` 中的 `@noAlias` directive,以及需要添加到 GraphQL 服务器 `validationRules` 数组中的验证函数。 在接下来的示例中,`hello` 查询将被允许在每个请求中调用 2 次,而通过直接在 `Mutation` type 上设置 `@noAlias` directive,所有的 mutation 将被限制为每次 mutation 只能调用 1 次。 ``` const express = require('express') const { graphqlHTTP } = require('express-graphql') const { buildSchema } = require('graphql') const { createValidation } = require('graphql-no-alias') // get the validation function and type definition of the declaration const { typeDefs, validation } = createValidation() //add type defintion to schema const schema = buildSchema(` ${typeDefs} type Query { hello: String @noAlias(allow:2) } type Mutation @noAlias { muteOne(n:Int):String } `) const app = express() app.use( '/graphql', graphqlHTTP({ schema: schema, rootValue: root, graphiql: true, validationRules: [validation] //add the validation function }) ) app.listen(4000) ``` ### Schema 设置 该声明可以用于 object `type`(Query 或 Mutation)或 type `fields`(特定的 query 或 mutation)。当该声明用于 `type` 时,它会影响该 type(Query 或 Mutation)的所有 fields。 #### Object type 用法 在接下来的示例中,**所有**查询将被限制为**仅一次调用**。 ``` const schema = buildSchema(` type Query @noAlias { getUser: User getFriends: [User]! } `) ``` 客户端请求: ``` query { getUser alias_get_user: getUser // Error - validation fails getFriends alias_get_friends: getFriends // Error - validation fails } ``` 该 directive 还接受一个参数 `allow`,用于声明允许的默认 alias 数量。 在接下来的示例中,所有查询将被允许调用 `3` 次(一次原始调用,两次 alias) ``` var schema = buildSchema(` type Query @noAlias(allow:3) { getUser: User getFriends: [User]! } `) ``` 在客户端: ``` query { getUser alias_2: getUser alias_3: getUser alias_4: getUser // Error - validation fails } ``` #### Field type 用法 在 type fields 上的用法与在 object type 上相同,区别在于,当与 object directive 结合使用时,field 上的 directive 将具有优先权。 在接下来的示例中,所有 query fields 将被允许进行 `3` 次批量调用,但 `getFriends` 查询除外,它只能被调用 `1` 次。 ``` var schema = buildSchema(` type Query @noAlias(allow:3) { getUser: User getFriends: [User]! @noAlias(allow:1) //same as @noAlias } `) ``` 客户端请求: ``` query { getUser alias_2: getUser alias_3: getUser getFriends alias_1: getFriends // Error - validation fails } ``` ### 自定义声明 可以自定义声明以拥有不同的名称和不同的默认 `allow` 值,还可以向其传递一个自定义错误函数,该函数会在验证失败时执行。 在接下来的示例中,`validation` 默认将允许对同一 field 调用 `3` 次,directive 名称将更改为 `NoBatchCalls`,并且会有一条自定义错误消息。 ``` const defaultAllow = 3 const directiveName = 'NoBatchCalls' const { typeDefs, validation } = createValidation({ defaultAllow, directiveName }) ``` 用法: ``` const schema = buildSchema(` type Query @noBatchCalls { getUser: User @noBatchCalls(allow:4) getFriends: [User]! } `) ``` ### 命令式配置 使用命令式配置,无需进行类型定义和 schema 修改。相反,我们使用一个配置对象。 这带来了更好的性能,因为不需要分析 `schema`(不需要寻找 directive)。 ``` const permissions = { Query: { '*': 2, // default value for all queries getAnotherUser: 5 // custom value for specific query }, Mutation: { '*': 1 //default value for all mutations } } const { validation } = createValidation({ permissions }) const schema = buildSchema(/* GraphQL */ ` type Query { getUser: User getAnotherUser: User } type User { name: String } `) const app = express() app.use( '/graphql', graphqlHTTP({ schema: schema, rootValue: root, graphiql: true, validationRules: [validation] //add the validation function }) ) app.listen(4000) ``` 请注意,当将 `permissions` 对象传递给配置时,schema directive 将被忽略。 ### 自定义错误消息 继续前面的示例,验证失败时报告的 `error` 消息也可以进行自定义。你可以返回一个 `GrahphQLError` 实例,或者只返回一个用作错误消息的 `string`。 ``` const { typeDefs, validation } = createValidation({errorFn:( typeName: string, //type name Query or Mutation fieldName: string, maxAllowed: number, node: FieldNode, ctx: ValidationContext ): GraphQLError { return new GraphQLError( `Hey! allowed number of calls for ${typeName}->${fieldName} has been exceeded (max: ${maxAllowed})` ) //or return string return 'custom message' } }) ``` ## Envelop 插件 如果你正在使用 [GraphQL Envelop](https://www.envelop.dev/)。我制作了一个使用此 directive 的[插件](packages/envelop/README.md)。 ## No Batched Queries 库 我还创建了另一个验证库:[No batched queries](https://github.com/ivandotv/graphql-no-batched-queries),它限制了每个请求可以发送的**所有**查询和 mutation 的数量。它与本验证配合得很好,因此你可以允许发送例如 3 个查询,然后使用 `noAlias` directive 禁止重复查询。 ### 许可证 本项目基于 MIT 许可证授权 - 详情请参阅 [LICENSE](LICENSE) 文件
标签:API安全, GraphQL, JSON输出, MITM代理, Syscall, Web开发, 接口防护, 数据可视化, 自动化攻击, 配置错误, 验证校验