Skip to content

arvi/graphql-armor

 
 

Repository files navigation

GraphQL Armor 🛡️

GraphQL Armor is a dead-simple yet highly customizable security middleware for various GraphQL server engines.

GraphQL-Armor banner

CI release e2e npm codecov

Contents

Supported GraphQL Engines

We support the following engines :

We additionnaly support the following engines through the Envelop plugin system :

  • GraphQL-Helix
  • Node.js HTTP
  • GraphQL-WS
  • GraphQL-SSE
  • Azure Functions
  • Cloudflare Workers
  • Google Cloud Functions
  • Lambda AWS
  • type-graphql
  • nexus
  • express-graphql

See here for more information about Envelop compatibility.

Installation

# npm
npm install -S @escape.tech/graphql-armor

# yarn
yarn add @escape.tech/graphql-armor

Getting Started

Refer to the Examples directory for specific implementation examples. (such as NestJS with Apollo Server)

Apollo Server

import { ApolloArmor } from '@escape.tech/graphql-armor';

const armor = new ApolloArmor();

const server = new ApolloServer({
  typeDefs,
  resolvers,
  ...armor.protect()
});

If you already have some plugins or validation rules, proceed this way:

import { ApolloArmor } from '@escape.tech/graphql-armor';

const armor = new ApolloArmor();
const protection = armor.protect()

const server = new ApolloServer({
  typeDefs,
  resolvers,
  ...protection,
  plugins: [...protection.plugins, myPlugin1, myPlugin2 ]
  validationRules: [, ...protection.validationRules, myRule1, myRule2 ]
});

GraphQL Yoga

import { EnvelopArmor } from '@escape.tech/graphql-armor';

const armor = new EnvelopArmor();
const protection = armor.protect()

async function main() {
  const server = createServer({
    schema,
    plugins: [...protection.plugins],
  });
  await server.start();
}

main();
import { EnvelopArmorPlugin } from '@escape.tech/graphql-armor';

async function main() {
  const server = createServer({
    schema,
    plugins: [EnvelopArmorPlugin()],
  });
  await server.start();
}

main();

Envelop

import { EnvelopArmor } from '@escape.tech/graphql-armor';

const armor = new EnvelopArmor();
const protection = armor.protect()

const getEnveloped = envelop({
  plugins: [otherPlugins, ...protection.plugins],
});
import { EnvelopArmorPlugin } from '@escape.tech/graphql-armor';

const getEnveloped = envelop({
  plugins: [otherPlugins, EnvelopArmorPlugin()],
});

Getting Started with configuration

GraphQL Armor is fully configurable in a per-plugin fashion.

View the per plugin configuration section for more information about how to configure each plugin separately.

import { ApolloArmor } from '@escape.tech/graphql-armor';

const armor = new ApolloArmor({
    costLimit: {
        maxCost: 1000,
    }
  }
});

Per plugin configuration

The provided values are the default values.

This section describes how to configure each plugin individually.

Stacktraces (Apollo Only)

This plugin is for Apollo Server only, and is enabled by default.

Stacktraces are managed by the Apollo configuration parameter debug which may have true as a default value in some setups. GraphQL Armor changes this default value to false.

For rolling back to Apollo's default parameter, you can use the following code:

import { ApolloArmor } from '@escape.tech/graphql-armor';

const armor = new ApolloArmor();
const server = new ApolloServer({
  typeDefs,
  resolvers,
  ...armor.protect(),
  debug: true // Ignore Armor's recommandation
});

Batched queries (Apollo Only)

This plugin is for Apollo Server only, and is enabled by default.

Batched queries are enabled by default, which makes DoS attacks easier by stacking expensive requests. We make them disabled by default.

For rolling back to Apollo's default parameter, you can use the following code:

import { ApolloArmor } from '@escape.tech/graphql-armor';

const armor = new ApolloArmor();
const server = new ApolloServer({
  typeDefs,
  resolvers,
  ...armor.protect(),
  allowBatchedHttpRequests: true // Ignore Armor's recommandations
});

Character Limit

This plugin is disabled by default.

It enforces a character limit on your GraphQL queries.

The limit is not applied to the whole HTTP body - multipart form data/file upload will still work.

For configuration details, refer to this README.

Cost Limit

This plugin is enabled by default.

It analyzes incoming GraphQL queries and applies a cost analysis algorithm to prevent resource overload by blocking too expensive requests (DoS attack attempts).

The cost computation is quite simple (and naive) at the moment but there are plans to make it evolve toward a extensive plugin with many features.

Configuration

{
  costLimit: {
    // enabled: true,
    maxCost: 5000, // maximum cost of a request before it is rejected
    objectCost: 2, // cost of retrieving an object
    scalarCost: 1, // cost of retrieving a scalar
    depthCostFactor: 1.5, // multiplicative cost of depth
    ignoreIntrospection: true, // by default, introspection queries are ignored.
    onAccept: [], // Callbacks that are ran whenever a Query is accepted
    onReject: [], // Callbacks that are ran whenever a Query is rejected
    propagateOnRejection: true, // When rejected, do you want to throw the error or report to the context?
  }
}

Field Suggestion

This plugin is enabled by default.

It will prevent suggesting fields in case of an erroneous request. Suggestions can lead to the leak of your schema even with disabled introspection, which can be very detrimental in case of a private API. One could use GraphDNA to recover an API schema even with disabled introspection, as long as field suggestions are enabled.

Example of such a suggestion:

Cannot query field "sta" on type "Media". Did you mean "stats", "staff", or "status"?

{
  blockFieldSuggestion: {
    // enabled: true,
  }
}

Aliases Limit

This plugin is enabled by default.

Limit the number of aliases in a document.

{
  maxAliases: {
    // enabled: true,
    n: 15,
    onAccept: [], // Callbacks that are ran whenever a Query is accepted
    onReject: [], // Callbacks that are ran whenever a Query is rejected
    propagateOnRejection: true, // When rejected, do you want to throw the error or report to the context?
  }
}

Directives Limit

This plugin is enabled by default.

Limit the number of directives in a document.

{
  maxDirectives: {
    // enabled: true,
    n: 50,
    onAccept: [], // Callbacks that are ran whenever a Query is accepted
    onReject: [], // Callbacks that are ran whenever a Query is rejected
    propagateOnRejection: true, // When rejected, do you want to throw the error or report to the context?
  }
}

Depth Limit

This plugin is enabled by default.

Limit the depth of a document.

{
  maxDepth: {
    // enabled: true,
    n: 6,
    onAccept: [], // Callbacks that are ran whenever a Query is accepted
    onReject: [], // Callbacks that are ran whenever a Query is rejected
    propagateOnRejection: true, // When rejected, do you want to throw the error or report to the context?
  }
}

Token Limit

This plugin is enabled by default.

Limit the number of GraphQL tokens in a document.

{
  maxTokens: {
    // enabled: true,
    n: 1000,
    onAccept: [], // Callbacks that are ran whenever a Query is accepted
    onReject: [], // Callbacks that are ran whenever a Query is rejected
    propagateOnRejection: true, // When rejected, do you want to throw the error or do nothing?
  }
}

Contributing

Ensure you have read the Contributing Guide before contributing.

To setup your project, make sure you run the install-dev.sh script.

git clone git@github.com:Escape-Technologies/graphql-armor.git
cd graphql-armor
bash ./install-dev.sh

We are using yarn as our package manager and the workspaces monorepo setup. Please read the associated documentation and feel free to open issues if you encounter problems when developing on our project!

About

🛡️ The missing GraphQL security security layer for Apollo GraphQL and Yoga / Envelop servers 🛡️

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 84.7%
  • JavaScript 12.6%
  • CSS 2.3%
  • Shell 0.4%