Introduce built-in armor security features (#6630)

This commit is contained in:
Denis Badurina 2025-03-20 12:42:57 +01:00 committed by GitHub
parent a003f781cb
commit 2397daaa00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 203 additions and 187 deletions

View file

@ -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',

View file

@ -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?
<Callout type="info">
Powered by [GraphQL
Armor](https://escape.tech/graphql-armor/docs/plugins/block-field-suggestions/).
</Callout>
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'

View file

@ -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.

View file

@ -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
}
})
```
<Callout>
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).
</Callout>
## 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
<Callout>
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.
</Callout>
## 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.
<Callout>
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.
</Callout>
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
})
```

View file

@ -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
<Callout type="info">Error masking is enabled by default in Hive Gateway.</Callout>
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)

View file

@ -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)
<Callout type="info">
Powered by [GraphQL Armor](https://escape.tech/graphql-armor/docs/plugins/max-depth).
</Callout>
## 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

View file

@ -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.
<Callout type="info">
Powered by [GraphQL Armor](https://escape.tech/graphql-armor/docs/plugins/max-tokens).
</Callout>
[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

View file

@ -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}'
}
]
})
```