Olá Habr! Apresento a você a tradução do artigo Protegendo sua API GraphQL de vulnerabilidades de segurança .
O GraphQL está rapidamente se tornando a escolha dos desenvolvedores que precisam criar uma API para seu aplicativo cliente. Mas, como todas as novas tecnologias, o GraphQL está sujeito a alguns riscos de segurança inerentes. Independentemente de você estar criando um projeto de terceiros ou um aplicativo corporativo em larga escala, você precisa se proteger contra essas ameaças.

Embora as ameaças listadas nesta postagem sejam específicas ao GraphQL, sua implementação apresentará um novo conjunto de ameaças que precisarão ser tratadas. Também é importante que você entenda as ameaças às quais todos os aplicativos em execução na rede estão expostos.
Ameaça: consultas grandes e profundamente aninhadas, caras para calcular
Solução : limitação da profundidade do aninhamento
A energia fornecida pelo GraphQL está associada a algumas novas ameaças à segurança. As mais comuns são consultas profundamente aninhadas, que levam a cálculos caros e enorme JSON, que podem afetar adversamente a rede e sua largura de banda.
A maneira correta de proteger sua API desse tipo de ataque é limitar a profundidade das solicitações, para que as solicitações profundas maliciosas sejam bloqueadas até que o resultado seja calculado.
O GraphQL Depth Limit fornece uma interface simples para limitar a profundidade da consulta.
import depthLimit from 'graphql-depth-limit' import express from 'express' import graphqlHTTP from 'express-graphql' import schema from './schema' const app = express() app.use('/graphql', graphqlHTTP((req, res) => ({ schema, validationRules: [ depthLimit(10) ] })))
Ameaça: solicitações de mutações vulneráveis à força bruta
Solução : limite o número de solicitações
Pesquisando logins e senhas é o truque mais antigo da história dos hackers. Na década passada, ocorreram tantos vazamentos de dados na Internet que um banco de dados de 772.904.991 e-mails exclusivos e 21.222.975 senhas exclusivas foi encontrado recentemente. Para verificar se as informações sobre seu e-mail e senha vazaram, Troy Hunt chegou a criar um site Eu fui Pwned , para o qual, entre outros, ele usou esse banco de dados.
Felizmente, você tem uma maneira fácil de tornar a pesquisa realmente difícil e cara para os invasores, o que o tornará um alvo menos atraente para eles.
O plug- in GraphQL Rate Limit permite especificar restrições para suas consultas de três maneiras diferentes: usando diretivas customizadas graphql-shield ou usando a função para limitar o número base de consultas.
Este plugin permitirá que você defina uma janela de tempo e um limite para o número de solicitações. Definir uma janela de tempo grande para solicitações muito vulneráveis, como logon e janelas de tempo pequenas para solicitações menos vulneráveis ajudará a manter uma experiência agradável para usuários comuns e se tornará um pesadelo para os invasores.
Crie uma diretiva para limitar o número de solicitações:
Aqui você precisará de um identificador exclusivo para cada solicitação. Você pode usar o endereço IP do usuário ou outro identificador, que é exclusivo para cada usuário e corresponde a cada solicitação.
const rateLimitDirective = createRateLimitDirective({ identifyContext: (context) => { return context.id }, })
Adicione uma diretiva ao seu esquema:
import { createRateLimitDirective } from 'graphql-rate-limit' export const schema = { typeDefs, resolvers, schemaDirectives: { rateLimit: rateLimitDirective, }, } export default schema
Por fim, adicione uma diretiva à sua consulta vulnerável:
# 60 Login(input: LoginInput!): User @rateLimit( window: "60s" max: 10 message: "You are doing that too often. Please wait 60 seconds before trying again." )
Ameaça: permitindo ao usuário influenciar dados específicos do usuário
Solução : tire esses dados de uma sessão do usuário sempre que possível
É fácil supor que, se você deseja permitir que o usuário atualize um recurso, vale a pena deixar que o usuário decida qual recurso ele deseja atualizar. Mas e se um usuário obtiver um identificador de recurso ao qual realmente não deveria ter acesso?
Suponha que tenhamos uma solicitação de mutação updateUser que permita ao usuário atualizar seu perfil.
mutation UpdateUser($input: {"id": "test123" , "email": "test@example.com"}) { UpdateUser(input: $input) { id firstName lastName } }
Se não houver proteção no servidor, o invasor, com uma lista de identificadores, poderá atualizar o endereço de email de qualquer usuário. A solução óbvia aqui é adicionar uma verificação para garantir que o ID do usuário atual corresponda ao ID nos campos de entrada.
Não faça isso:
function updateUser({ id, email }) { return User.findOneAndUpdate({ _id: id }, { email }) .catch(error => { throw error; }); }
Uma maneira menos óbvia, mas correta, de resolver esse problema é impedir que o identificador seja usado como entrada e usar o identificador de usuário do objeto de contexto.
Faça o seguinte:
function updateUser({ email }, context) { return User.findOneAndUpdate({ _id: context.user._id }, { email }) .catch(error => { throw error; }); }
Talvez este seja um exemplo bastante trivial, mas executar essas ações para cada um dos objetos com os quais o usuário interage diretamente pode protegê-lo de muitos erros arriscados.
Realização de várias consultas caras simultaneamente
Solução : limite de custo da consulta
Ao atribuir um preço a cada solicitação e indicar o preço máximo por solicitação, podemos nos proteger contra invasores que podem tentar atender a muitas solicitações caras ao mesmo tempo.
O plug-in de análise de custos do GraphQL é uma maneira fácil de especificar o custo das consultas e o limite máximo de custos.
Determine o custo máximo:
app.use( '/graphql', graphqlExpress(req => { return { schema, rootValue: null, validationRules: [ costAnalysis({ variables: req.body.variables, maximumCost: 1000, }), ], } }) )
Determine o custo de cada solicitação:
Query: { Article: { multipliers: ['limit'], useMultipliers: true, complexity: 3, }, }
Ameaça: divulgando detalhes de implementação do GraphQL
Solução : desabilite a introspecção no código "combate"
O GraphQL é uma ferramenta de desenvolvimento extremamente útil. É tão poderoso que até documenta seu esquema, solicitações e assinaturas para você. Essas informações podem ser uma mina de ouro para invasores que desejam encontrar vulnerabilidades em seu aplicativo.
O plugin GraphQL Display Introspection impedirá que o seu esquema vaze para o público. Basta importar o plug-in e aplicá-lo às suas regras de validação.
import express from 'express'; import bodyParser from 'body-parser'; import { graphqlExpress } from 'graphql-server-express'; + import NoIntrospection from 'graphql-disable-introspection'; const myGraphQLSchema =