Como criar uma API GraphQL segura com Node.js
Lawrence Eagles
29 de março de 2022
0 minutos de leituraO GraphQL oferece recursos de segurança integrados, como validação e verificação de tipos. No entanto, ele não resolve totalmente as questões de segurança relacionadas às APIs. Neste artigo, vamos aprender a proteger APIs GraphQL criando uma aplicação simples em Node.js com Fastify e GraphQL.
De acordo com a documentação oficial, GraphQL é uma linguagem de consulta para grafos para APIs e um runtime que executa essas consultas com nossos dados. O GraphQL descreve com clareza os dados da nossa API e permite criar APIs rápidas e flexíveis, dando aos clientes controle total para especificar os dados de que precisam. Assim como o REST, o GraphQL funciona sobre HTTP, é independente de banco de dados e pode ser usado com qualquer linguagem de back-end ou cliente.
Fastify é um framework para Node.js baseado em plugins, altamente eficiente e com excelente desempenho, ideal para criar servidores HTTP rápidos. Inspirado em Hapi e Express, o Fastify é uma alternativa mais amigável para desenvolvedores e com melhor desempenho, além de ter baixo overhead.
O Fastify oferece suporte ao GraphQL por meio do plugin Mercurius. O plugin Mercurius é um adaptador GraphQL configurável para Fastify, e vamos conhecê-lo melhor nas próximas seções.
Vamos começar pelos pré-requisitos.
Pré-requisitos
Estes são os pré-requisitos para acompanhar este artigo:
Node.js versão 12 ou superior
Conhecimentos básicos de JavaScript
Conhecimentos básicos de GraphQL
Primeiros passos
Para começar, precisamos criar um servidor básico em Node.js.
Crie uma pasta para o projeto e, dentro dela, execute o código abaixo na interface de linha de comando (CLI). Isso vai configurar nossa aplicação e instalar as dependências necessárias.
Este projeto usa uma versão específica do Mercurius. Para instalá-la, execute o seguinte comando:
Em seguida, habilitamos os módulos ES6 para usar o sistema padrão de módulos JavaScript em vez do commonJS. Para isso, adicionamos "type": "module" ao arquivo package.json.
Depois, atualizamos os scripts do NPM com um comando para iniciar o servidor Node.js. Para isso, abrimos o arquivo package.json e editamos a seção de scripts da seguinte forma:
Observe que o trecho --es-module-specifier-resolution=node é necessário para habilitar a interoperabilidade entre os módulos ES e os módulos commonJS do Node.
Agora, crie um diretório src no diretório raiz. Dentro dele, crie uma pasta graphql com os arquivos schema.js e resolvers.js. Adicione o código a seguir ao arquivo schema.js:
Agora, adicione o código a seguir ao arquivo resolvers.js:
Na próxima seção, vamos atualizar o schema.js e adicionar o arquivo resolvers.js. Mas precisamos criá-los agora com algum código básico, pois eles são necessários para que nosso servidor funcione corretamente.
No diretório src, crie um arquivo index.js com o seguinte código:
O código acima cria um servidor Fastify principal e registra o plugin Mercurius com as opções: schema, resolvers, graphiql e queryDepth.
Agora podemos iniciar o servidor executando npm run dev. A saída será:
Agora podemos ver que nosso servidor está funcionando. Na próxima seção, vamos começar a criar nossas APIs de blog com GraphQL.
Como criar uma API de blog segura com Fastify e GraphQL
Há várias estratégias para proteger uma API. Entre elas estão:
Autenticação e autorização: A autenticação confirma se uma pessoa é quem diz ser, enquanto a autorização trata das permissões. A autenticação determina se alguém pode fazer login e, em seguida, mantém essa pessoa identificada. Já a autorização determina quais permissões são atribuídas a uma pessoa identificada e controla se ela pode realizar operações como criar, ler, atualizar ou excluir.
Ocultação de erros: Não revelar informações exatas sobre um erro do servidor evita que detalhes que possam expor vulnerabilidades sejam fornecidos ao cliente sem intenção.
Limite de profundidade das consultas: Definir uma profundidade máxima para as consultas GraphQL. Consultas muito aninhadas são perigosas porque exigem muitos recursos e têm um alto custo de processamento. Como consequência, podem derrubar nossas APIs.
Sanitização e validação de entradas: Usar técnicas padrão de segurança para a Web para impedir que usuários enviem dados maliciosos. Vamos aproveitar a validação integrada do GraphQL na nossa aplicação.
Neste artigo, vamos criar e proteger nossas APIs usando as estratégias acima, com o Mercurius e o plugin Mercurius Auth.
Para o que precisamos, o plugin Mercurius Auth tem dois recursos principais. Primeiro, ele permite definir diretivas de autenticação personalizadas para campos do nosso schema. Essas diretivas são strings usadas como identificadores para os campos protegidos no schema.
Além disso, ele permite aplicar políticas de autenticação personalizadas a esses campos protegidos ao fazer uma solicitação GraphQL.
Vamos começar criando dados simulados. No diretório src, crie uma pasta data com um arquivo index.js que contenha o código abaixo:
Em seguida, configuramos o schema substituindo o código básico do arquivo schema.js pelo código a seguir:
No código acima, criamos nosso schema GraphQL e definimos diretivas de autenticação para os campos user e users. Em seguida, vamos aplicar políticas personalizadas a esses campos protegidos.
Agora, adicionamos nossos resolvers substituindo o código básico do arquivo resolvers.js pelo código a seguir:
O código acima contém resolvers para lidar com as consultas user, users e login.
Por fim, precisamos adicionar uma política de autenticação personalizada registrando o plugin Mercurius Auth. Para isso, no arquivo index.js do diretório src, adicionamos o código a seguir abaixo do comentário register auth policy, que está na linha 19:
Na política personalizada acima, o método authContext recupera o token do usuário nos cabeçalhos, enquanto o método applyPolicy contém políticas personalizadas de autenticação e autorização.
Além disso, quando a autenticação ou autorização de um usuário falha, geramos um erro com uma mensagem genérica, como “Ocorreu um erro. Tente novamente!”. Essa mensagem é exibida no lugar de uma mensagem detalhada de erro do servidor, que poderia expor vulnerabilidades existentes.
Com isso, concluímos nosso trabalho. Na próxima seção, vamos testar nossas APIs.
Como testar a API
Primeiro, iniciamos o servidor executando npm run dev no diretório raiz. Em seguida, acessamos o GraphQL Playground em http://localhost:4500/playground.
Agora, ao consultar qualquer API protegida, como users ou user, recebemos um erro, como mostrado abaixo:

Para que nossa consulta seja bem-sucedida, precisamos nos autenticar. Vamos fazer login para obter um token.
Para fazer login, abra uma nova aba no Playground e execute a seguinte consulta:
Se a consulta for bem-sucedida, um token será gerado e retornado, como mostrado na imagem abaixo.

Observe que os dados do usuário usados para fazer login já estão no arquivo index.js, no diretório data.
Uma tentativa de fazer login com dados de usuário que não estão nesse arquivo (como o nome de usuário “John1Doe”) resulta em um erro, como mostrado abaixo:

Agora, passando nosso token nos cabeçalhos como x-user, podemos consultar nossas APIs protegidas com sucesso, como mostrado abaixo. Copie o token recebido na consulta de login e use-o como valor de x-user.
Consulta users
Veja um exemplo de consulta users:
Adicione seu token como valor do parâmetro do cabeçalho HTTP x-user, como mostrado abaixo:
O resultado será:

Consulta user
Veja um exemplo de consulta de user:
Adicione seu token como valor do parâmetro do cabeçalho HTTP x-user, como mostrado abaixo:
O resultado será:

Conclusão
Neste artigo, vimos como é fácil proteger APIs GraphQL usando Fastify e GraphQL para criar uma aplicação simples em Node.js.
Como vimos, o GraphQL conta com alguns recursos de segurança integrados, como validação e verificação de tipos. No entanto, a flexibilidade e o poder que permitem aos usuários solicitar dados sob demanda fazem da segurança uma preocupação fundamental.
Este artigo também abordou algumas estratégias para proteger APIs GraphQL: autenticação e autorização, limite de profundidade das consultas, ocultação de erros e sanitização e validação de entradas.
Essas estratégias de segurança são muito eficazes, mas podemos reforçar a proteção com outros métodos, como limites de tempo para consultas e limites de taxa, que definem a frequência com que um cliente pode consultar uma API em cada período.
Comece a participar de desafios de Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.
Experimente nossa ferramenta gratuita on-line de verificação de código JavaScript para ver como o Snyk Code analisa seu código em busca de problemas de segurança e qualidade.
