From 2397daaa0041dc12675079c453812732fd49fd16 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 20 Mar 2025 12:42:57 +0100 Subject: [PATCH] Introduce built-in armor security features (#6630) --- .../gateway/other-features/security/_meta.ts | 2 +- .../security/block-field-suggestions.mdx | 30 ++- .../security/character-limit.mdx | 2 +- .../security/disable-introspection.mdx | 69 +++---- .../gateway/other-features/security/index.mdx | 195 +++++++++--------- .../other-features/security/max-depth.mdx | 32 ++- .../other-features/security/max-tokens.mdx | 36 ++-- .../other-features/security/rate-limiting.mdx | 24 +-- 8 files changed, 203 insertions(+), 187 deletions(-) diff --git a/packages/web/docs/src/content/gateway/other-features/security/_meta.ts b/packages/web/docs/src/content/gateway/other-features/security/_meta.ts index b8825b9ce..3e3aeefad 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/_meta.ts +++ b/packages/web/docs/src/content/gateway/other-features/security/_meta.ts @@ -4,7 +4,7 @@ export default { 'csrf-prevention': 'CSRF Prevention', 'rate-limiting': 'Rate Limiting', 'demand-control': 'Demand Control', - 'disable-introspection': 'Introspection', + 'disable-introspection': 'Disable Introspection', https: 'HTTPS', 'hmac-signature': 'HMAC Signature', 'aws-sigv4': 'AWS Signature V4', diff --git a/packages/web/docs/src/content/gateway/other-features/security/block-field-suggestions.mdx b/packages/web/docs/src/content/gateway/other-features/security/block-field-suggestions.mdx index 6868c059d..1f68d1da8 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/block-field-suggestions.mdx +++ b/packages/web/docs/src/content/gateway/other-features/security/block-field-suggestions.mdx @@ -7,21 +7,37 @@ import { Callout } from '@theguild/components' # Block Field Suggestions This is a feature that allows you to prevent **returning field suggestions** and **leaking your -schema** to unauthorized actors provided by -[GraphQL Armor](https://escape.tech/graphql-armor/docs/plugins/block-field-suggestions/) +schema** to unauthorized actors. In production, this can lead to leaking schema information even if +the introspection is disabled. -In production, this can lead to Schema leak even if the introspection is disabled. +## Basic Configuration -## How to use? + + Powered by [GraphQL + Armor](https://escape.tech/graphql-armor/docs/plugins/block-field-suggestions/). + -Install the plugin: +Hive Gateway ships with the basic "block field suggestion" security feature. You can enable it by +setting the `blockFieldSuggestions` option to `true`. + +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + blockFieldSuggestions: true +}) +``` + +## Advanced Configuration + +The built-in configuration options are limited and should be enough for most use-cases. However, if +you need more control, you can configure more by installing the +[GraphQL Armor Block Field Suggestions plugin](https://escape.tech/graphql-armor/docs/plugins/block-field-suggestions/). ```sh npm2yarn npm install @escape.tech/graphql-armor-block-field-suggestions ``` -Then, add it to your plugins: - ```ts filename="gateway.config.ts" import { blockFieldSuggestionsPlugin } from '@escape.tech/graphql-armor-block-field-suggestions' import { defineConfig } from '@graphql-hive/gateway' diff --git a/packages/web/docs/src/content/gateway/other-features/security/character-limit.mdx b/packages/web/docs/src/content/gateway/other-features/security/character-limit.mdx index 59188a07d..0104a45db 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/character-limit.mdx +++ b/packages/web/docs/src/content/gateway/other-features/security/character-limit.mdx @@ -4,7 +4,7 @@ searchable: false import { Callout } from '@theguild/components' -# Character Limit +# Character Limit Plugin **Limit** number of **characters** in a GraphQL query document. diff --git a/packages/web/docs/src/content/gateway/other-features/security/disable-introspection.mdx b/packages/web/docs/src/content/gateway/other-features/security/disable-introspection.mdx index a06ddb33e..fd61ab97f 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/disable-introspection.mdx +++ b/packages/web/docs/src/content/gateway/other-features/security/disable-introspection.mdx @@ -6,7 +6,7 @@ searchable: false import { Callout } from '@theguild/components' -# Introspection +# Disable Introspection A powerful feature of GraphQL is schema introspection. This feature is used by GraphiQL for exploring the schema and also by tooling such as @@ -16,15 +16,23 @@ client/frontend code. GraphQL schema introspection is also a feature that allows clients to ask a GraphQL server what GraphQL features it supports (e.g. defer/stream or subscriptions). -## Disabling Introspection +However, you may want to not expose your schema to the outside world. You can disable schema +introspection with the `disableIntrospection` option. + +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + disableIntrospection: { + disableIf: () => true + } +}) +``` - If your goal is to avoid unknown actors from reverse-engineering your GraphQL - schema and executing arbitrary operations, it is highly recommended to use - persisted operations. - -[Learn more about persisted operations.](/docs/gateway/persisted-documents) - + If your goal is to avoid unknown actors from reverse-engineering your GraphQL schema and executing + arbitrary operations, it is highly recommended to use [persisted + operations](/docs/gateway/persisted-documents). ## Disable Introspection based on the GraphQL Request @@ -33,7 +41,7 @@ Sometimes you want to allow introspectition for certain users. You can access th and determine based on that whether introspection should be enabled or not. E.g. you can check the headers. -```ts filename="gateway.config.ts" {7} +```ts filename="gateway.config.ts" {6} import { defineConfig } from '@graphql-hive/gateway' export const gatewayConfig = defineConfig({ @@ -45,45 +53,28 @@ export const gatewayConfig = defineConfig({ }) ``` -## Disabling Field Suggestions - - - The [`graphql-armor`](https://github.com/Escape-Technologies/graphql-armor) plugin is a security layer that help you protect your GraphQL server from malicious queries. - It allows you to configure various security features such as character limit or blocking field suggestions. - For more information about `graphql-armor` features, you can refer to the [documentation for the plugin](/docs/gateway/other-features/security/block-field-suggestions). - -Here is an example of how to use `graphql-armor` to disable introspection and block field -suggestions. - - +## Blocking Field Suggestions When executing invalid GraphQL operation the GraphQL engine will try to construct smart suggestions that hint typos in the executed GraphQL document. This can be considered a security issue, as it can leak information about the GraphQL schema, even if introspection is disabled. - - If your goal is to avoid unknown actors from reverse-engineering your GraphQL - schema and executing arbitrary operations, it is highly recommended to use - persisted operations. +Tools like [Clairvoyance](https://github.com/nikitastupin/clairvoyance) can exploit the smart +suggestions and brute-force obtaining the GraphQL schema even if the introspection has been +disabled. -[Learn more about persisted operations.](/docs/gateway/persisted-documents) +[Enabling the `blockFieldSuggestions` option](/docs/gateway/other-features/security/block-field-suggestions) +will disable these smart suggestions and therefore prevent schema leaking. - +Combined with the `disableIntrospection` option, you can make your schema more secure. -Disabling the "did you mean x" suggestion feature can be achieved via the -`blockFieldSuggestionsPlugin` from -[`graphql-armor`](https://github.com/Escape-Technologies/graphql-armor). - -```sh npm2yarn -npm i @escape.tech/graphql-armor-block-field-suggestions -``` - -```ts filename="Disabling the 'did you mean x' suggestion feature with a plugin" {2, 7} -import { blockFieldSuggestionsPlugin } from '@escape.tech/graphql-armor-block-field-suggestions' -import { defineConfig, useDisableIntrospection } from '@graphql-hive/gateway' +```ts filename="gateway.config.ts" {7} +import { defineConfig } from '@graphql-hive/gateway' export const gatewayConfig = defineConfig({ - disableIntrospection: true, - plugins: pluginCtx => [blockFieldSuggestionsPlugin()] + disableIntrospection: { + disableIf: () => true + }, + blockFieldSuggestions: true }) ``` diff --git a/packages/web/docs/src/content/gateway/other-features/security/index.mdx b/packages/web/docs/src/content/gateway/other-features/security/index.mdx index f0703e608..50dbfb758 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/index.mdx +++ b/packages/web/docs/src/content/gateway/other-features/security/index.mdx @@ -13,13 +13,79 @@ securing your server is now as easy as pie! Hive Gateway has a built-in security you to secure your Gateway. But in most of time, this security layer is not enough or needed to be customized for your use case. -## Protection against Malicious GraphQL Operations +A handy tool for analyzing your existing GraphQL operations and finding the best defaults for your +use case is [`graphql-inspector`](https://www.the-guild.dev/graphql/inspector). Using the +[`graphql-inspector audit` command](https://the-guild.dev/graphql/inspector/docs/commands/audit) you +can find potential security improvements in your schema. -One of the main benefits of GraphQL is that data can be requested individually. However, this also -introduces the possibility for attackers to send operations with deeply nested selection sets that -could block other requests being processed. Fortunately, infinite loops are not possible by design -as a fragment cannot self-reference itself. Unfortunately, that still does not prevent possible -attackers from sending selection sets that are hundreds of levels deep. +## Persisted Operations + +Instead of allowing any arbitrary GraphQL operation in production usage, we could use an allow-list +of operations that the server is allowed to execute. We can collect such a list by scanning the +code-base and extracting the list of operations. + +[Configuring the `persistedDocuments` option](/docs/gateway/persisted-documents) you're able to +define a list of persisted operations that are allowed to execute. + +## Limit Max Tokens + +Parsing a GraphQL operation document is a very expensive and compute intensitive operation that +blocks the JavaScript event loop. If an attacker sends a very complex operation document with slight +variations over and over again he can easily degrade the performance of the GraphQL server. Because +of the variations simply having an LRU cache for parsed operation documents is not enough. + +A potential solution is to limit the maximal allowed count of tokens within a GraphQL document. + +In computer science, lexical analysis, lexing or tokenization is the process of converting a +sequence of characters into a sequence of lexical tokens. + +E.g. given the following GraphQL operation. + +```graphql +query { + me { + id + user + } +} +``` + +The tokens are `query`, `{`, `me`, `{`, `id`, `user`, `}` and `}`. Having a total count of 8 tokens. + +The optimal maximum token count for your application depends on the complexity of the GrapHQL +operations and documents. Usually 800-2000 tokens seems like a sane default. + +[Configuring the `maxTokens` option](/docs/gateway/other-features/security/max-tokens) you're able +to limit the amount of allowed tokens per operation and automatically abort any further processing +of a GraphQL operation document that exceeds the limit. + +This option can be combined with +[the Character Limit Plugin](/docs/gateway/other-features/security/character-limit) that limits the +number of characters in the query and mutation documents. + +## HMAC Signing + +When you have multiple subgraphs and a gateway, you might want to ensure that the requests to the +subgraphs are trusted and signed by the gateway. This is handy in case your want to ensure that the +requests to the subgraphs are trusted and signed by the gateway, and no other entity can execute +requests to the subgraph. + +In case of any missing signature, tampering or unauthorized access, the subgraph services will +reject the request. + +We recommend using HMAC signing for requests between the Hive Gateway and the upstream in cases +where authentication plugins are involved, in order to ensure the gateway is the only entity that +can execute requests to the subgraph on behalf of the end-users. + +[Configure the `hmacSignature` option](/docs/gateway/other-features/security/hmac-signature) to +perform requesting signing and verification. + +## Query Depth Limiting + +Attackers can send operations with deeply nested selection sets that could block other requests +being processed. Fortunately, infinite loops are not possible by design as a fragment cannot +self-reference itself; however, that still does not prevent possible attackers from sending +selection sets that are hundreds of levels deep. The following schema: @@ -71,93 +137,18 @@ query { } ``` -There are a few measurements you can use for preventing the execution of such operations. +[Configuring the `maxDepth` option](/docs/gateway/other-features/security/max-depth) can prevent +malicious API users executing GraphQL operations with deeply nested selection sets. You need to +tweak the maximum depth an operation selection set is allowed to have based on your schema and +needs, as it could vary between users. -A handy tool for analyzing your existing GraphQL operations and finding the best defaults for your -use case is [`graphql-inspector`](https://www.the-guild.dev/graphql/inspector). - -Learn more about `graphql-inspector audit` -[here](https://the-guild.dev/graphql/inspector/docs/essentials/audit). - -### Persisted Operations - -Instead of allowing any arbitrary GraphQL operation in production usage, we could use an allow-list -of operations that the server is allowed to execute. We can collect such a list by scanning the -code-base and extracting the list of operations. - -[Learn more how to configure persisted operations](/docs/gateway/persisted-documents) - -### Reject Malicious Operation Documents - -Parsing a GraphQL operation document is a very expensive and compute intensitive operation that -blocks the JavaScript event loop. If an attacker sends a very complex operation document with slight -variations over and over again he can easily degrade the performance of the GraphQL server. Because -of the variations simply having an LRU cache for parsed operation documents is not enough. - -A potential solution is to limit the maximal allowed count of tokens within a GraphQL document. - -In computer science, lexical analysis, lexing or tokenization is the process of converting a -sequence of characters into a sequence of lexical tokens. - -E.g. given the following GraphQL operation. - -```graphql -query { - me { - id - user - } -} -``` - -The tokens are `query`, `{`, `me`, `{`, `id`, `user`, `}` and `}`. Having a total count of 8 tokens. - -The optimal maximum token count for your application depends on the complexity of the GrapHQL -operations and documents. Usually 800-2000 tokens seems like a sane default. - -You can limit the amount of allowed tokens per operation and automatically abort any further -processing of a GraphQL operation document that exceeds the limit with the -[Max Tokens Plugin](/docs/gateway/other-features/security/max-tokens). - -Also this can be combined with -[Character Limit](/docs/gateway/other-features/security/character-limit) that limits the number of -characters in the query and mutation documents. - -### Gateway -> Subgraph HMAC Signing - -When you have multiple subgraphs and a gateway, you might want to ensure that the requests to the -subgraphs are trusted and signed by the gateway. This is handy in case your want to ensure that the -requests to the subgraphs are trusted and signed by the gateway, and no other entity can execute -requests to the subgraph. - -In case of any missing signature, tampering or unauthorized access, the subgraph services will -reject the request. - -We recommend using HMAC signing for requests between the Hive Gateway and the upstream in cases -where authentication plugins are involved, in order to ensure the gateway is the only entity that -can execute requests to the subgraph on behalf of the end-users. - -You can use the [HMAC Signature plugin](/docs/gateway/other-features/security/hmac-signature) to -perform requesting signing and verification. - -### Query Depth Limiting - -Sometimes persisted operations cannot be used. E.g. if you are building an API that is used by third -party users. However, we can still apply some protection. - -[Learn more about Max Depth plugin here](/docs/gateway/other-features/security/max-depth) - -This can prevent malicious API users executing GraphQL operations with deeply nested selection sets. -You need to tweak the maximum depth an operation selection set is allowed to have based on your -schema and needs, as it could vary between users. - -### Rate Limiting +## Rate Limiting Rate-limiting is a common practice with APIs, and with GraphQL it gets more complicated because of the flexibility of the graph and the ability to choose what fields to query. -The [Rate Limit Plugin](/docs/gateway/other-features/security/rate-limiting) can be used to limit -access to resources by field level. +[Configuring the `rateLimiting` option](/docs/gateway/other-features/security/rate-limiting) can be +used to limit access to resources by field level. ## Prevent unwanted HTTP requests @@ -168,37 +159,41 @@ browsers to give a web application running at one origin, access to selected res different origin. A web application makes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. -[Learn more about CORS plugin here](/docs/gateway/other-features/security/cors) +[Configuring the `cors` option](/docs/gateway/other-features/security/cors) you can control the +access to the Hive Gateway from browsers. ### CSRF Prevention Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated. -[Learn more about CSRF Prevention plugin here](/docs/gateway/other-features/security/csrf-prevention) +[Configuring the `csrfPrevention` option](/docs/gateway/other-features/security/csrf-prevention) +allows you to prevent these kind of attacks by adding a CSRF token. ## Prevent Leaking Sensitive Information ### Disable Schema Introspection If your schema includes sensitive information that you want to hide from the outside world, -disabling the schema introspection is a possible solution. The -[Disable Introspection Plugin](/docs/gateway/other-features/security/disable-introspection) plugin -solves that in a single line of code! +disabling the schema introspection is a possible solution. +[Configure the `disableIntrospection` option](/docs/gateway/other-features/security/disable-introspection) +and control the access to schema introspection. ### Block Field Suggestions -Field suggestions are a feature of GraphQL that allows the client to request the server to suggest -fields that can be queried. This is a very useful feature for developers using GraphQL, but it can -also be used by attackers to discover the schema of the server. +Field suggestions are a feature of GraphQL that assumes which fields did the client wanted to query +in case of typos or missing fields. This is a very useful feature for developers using GraphQL, but +it can also be used by attackers to discover the schema of the server. -You can block field suggestions with the -[Block Field Suggestions Plugin](/docs/gateway/other-features/security/block-field-suggestions). +[Enabling the `blockFieldSuggestion` option](/docs/gateway/other-features/security/block-field-suggestions) +you can block field suggestions. -### Error Masking (enabled by default) +### Error Masking + +Error masking is enabled by default in Hive Gateway. In most GraphQL servers any thrown error or rejected promise will result in the original error leaking to the outside world. Some frameworks have custom logic for catching unexpected errors and mapping them to an unexpected error instead. In Hive Gateway, this is enabled by default. -[Learn more about Error Masking](/docs/gateway/logging-and-error-handling) +[Learn more about Error Handling and how masking works.](/docs/gateway/logging-and-error-handling) diff --git a/packages/web/docs/src/content/gateway/other-features/security/max-depth.mdx b/packages/web/docs/src/content/gateway/other-features/security/max-depth.mdx index be98850f3..6b5700923 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/max-depth.mdx +++ b/packages/web/docs/src/content/gateway/other-features/security/max-depth.mdx @@ -6,22 +6,36 @@ import { Callout } from '@theguild/components' # Max Depth -**Limit** the **depth** of a GraphQL document. +**Limit** the **depth** of a GraphQL document. It is used to prevent too large queries that could +lead to overfetching or **DOS attack**. -It is used to prevent too large queries that could lead to overfetching or **DOS attack**. +## Basic Configuration -[Provided by GraphQL Armor](https://escape.tech/graphql-armor/docs/plugins/max-depth) + + Powered by [GraphQL Armor](https://escape.tech/graphql-armor/docs/plugins/max-depth). + -## How to use? +Hive Gateway ships with the basic "max depth" security features. You can enable it by setting the +`maxDepth` option to `true` or configure the allowed depth by passing a `number` to the option. -Install the plugin: +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + maxDepth: true // defaults to 6 +}) +``` + +## Advanced Configuration + +The built-in configuration options are limited and should be enough for most use-cases. However, if +you need more control, you can configure more by installing the +[GraphQL Armor Max Depth plugin](https://escape.tech/graphql-armor/docs/plugins/max-depth/). ```sh npm2yarn npm install @escape.tech/graphql-armor-max-depth ``` -Then, add it to your plugins: - ```ts filename="gateway.config.ts" import { maxDepthPlugin } from '@escape.tech/graphql-armor-max-depth' import { defineConfig } from '@graphql-hive/gateway' @@ -49,7 +63,3 @@ export const gatewayConfig = defineConfig({ ] }) ``` - -## References - -- https://github.com/advisories/GHSA-mh3m-8c74-74xh diff --git a/packages/web/docs/src/content/gateway/other-features/security/max-tokens.mdx b/packages/web/docs/src/content/gateway/other-features/security/max-tokens.mdx index b1ef48384..1855cd499 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/max-tokens.mdx +++ b/packages/web/docs/src/content/gateway/other-features/security/max-tokens.mdx @@ -6,25 +6,38 @@ import { Callout } from '@theguild/components' # Max Tokens -**Limit** the number of **tokens** in a GraphQL document. +**Limit** the number of **tokens** in a GraphQL document. It is used to prevent **DOS attack**, +**heap overflow** or **server overloading**. The token limit is often limited by the GraphQL parser, +but this is not always the case and would lead to a fatal heap overflow. -It is used to prevent **DOS attack**, **heap overflow** or **server overloading**. +## Basic Configuration -The token limit is often limited by the graphql parser, but this is not always the case and would -lead to a fatal heap overflow. + + Powered by [GraphQL Armor](https://escape.tech/graphql-armor/docs/plugins/max-tokens). + -[Provided by GraphQL Armor](https://escape.tech/graphql-armor/docs/plugins/max-tokens) +Hive Gateway ships with the basic "max tokens" security features. You can enable it by setting the +`maxTokens` option to `true` or configure the amount of allowed tokens by passing a `number` to the +option. -## How to use? +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' -Install the plugin: +export const gatewayConfig = defineConfig({ + maxTokens: true // defaults to 1000 +}) +``` + +## Advanced Configuration + +The built-in configuration options are limited and should be enough for most use-cases. However, if +you need more control, you can configure more by installing the +[GraphQL Armor Max Tokens plugin](https://escape.tech/graphql-armor/docs/plugins/max-tokens/). ```sh npm2yarn npm install @escape.tech/graphql-armor-max-tokens ``` -Then, add it to your plugins: - ```ts filename="gateway.config.ts" import { maxTokensPlugin } from '@escape.tech/graphql-armor-max-tokens' import { defineConfig } from '@graphql-hive/gateway' @@ -52,8 +65,3 @@ export const gatewayConfig = defineConfig({ ] }) ``` - -## References - -- https://github.com/graphql/graphql-js/pull/3684 -- https://github.com/advisories/GHSA-p4qx-6w5p-4rj2 diff --git a/packages/web/docs/src/content/gateway/other-features/security/rate-limiting.mdx b/packages/web/docs/src/content/gateway/other-features/security/rate-limiting.mdx index ebc9d8f70..8b0f7c3cd 100644 --- a/packages/web/docs/src/content/gateway/other-features/security/rate-limiting.mdx +++ b/packages/web/docs/src/content/gateway/other-features/security/rate-limiting.mdx @@ -2,8 +2,6 @@ searchable: false --- -import { Callout } from '@theguild/components' - # Rate Limiting Rate limiting is a technique for reducing server load by limiting the number of requests that can be @@ -17,18 +15,16 @@ You can use rate limiting feature in order to limit the rate of calling queries import { defineConfig } from '@graphql-hive/gateway' export const gatewayConfig = defineConfig({ - rateLimiting: { - rules: [ - { - type: 'Query', - field: 'foo', - max: 5, // requests limit for a time period - ttl: 5000, // time period - // You can use any value from the context - identifier: '{context.headers.authorization}' - } - ] - } + rateLimiting: [ + { + type: 'Query', + field: 'foo', + max: 5, // requests limit for a time period + ttl: 5000, // time period + // You can use any value from the context + identifier: '{context.headers.authorization}' + } + ] }) ```