Segurança de GraphQL
A aula aborda os principais riscos de segurança em APIs GraphQL, incluindo queries complexas e batching que podem causar ataques de negação de serviço, autorização mal implementada que expõe dados sensíveis, e exposição indevida de informações via introspecção e campos não autorizados. São apresentadas práticas de mitigação como limitação de profundidade, análise de custo de queries, uso de dataloaders e validação de autorização em nível de campo.
GraphQL é uma linguagem de consulta e manipulação de dados para APIs que oferece grande flexibilidade aos clientes, mas essa mesma flexibilidade introduz desafios de segurança únicos. Diferente de REST, onde os endpoints são fixos, no GraphQL o cliente pode solicitar exatamente os dados que deseja, o que pode ser explorado para sobrecarregar o servidor ou acessar informações não autorizadas. Nesta aula, exploraremos quatro áreas críticas de segurança em GraphQL: queries complexas, batching, autorização e exposição indevida. Para cada tópico, discutiremos os riscos, exemplos de ataques e boas práticas de mitigação.
Queries complexas
Queries complexas são consultas GraphQL que exigem processamento intenso no servidor, seja por aninhamento profundo, solicitação de muitos campos relacionados ou uso de operações caras. Um atacante pode explorar isso para causar negação de serviço (DoS) ao enviar uma única query que força o servidor a buscar e combinar grandes volumes de dados. Por exemplo, uma query como a abaixo pode tentar resolver recursivamente uma relação de amizade até uma profundidade excessiva:
query {
user(id: 1) {
friends {
friends {
friends {
name
}
}
}
}
}
Para mitigar esse risco, é essencial implementar limites de profundidade máxima de queries (ex.: 5 níveis) e análise de custo (query cost analysis), onde cada campo tem um peso e a soma total é limitada. Ferramentas como graphql-depth-limit e graphql-validation-complexity ajudam a aplicar essas restrições. Além disso, o uso de paginação com first/last e limites de itens por página evita que uma única query retorne milhões de registros.
Batching
Batching (ou lotes) refere-se à capacidade do GraphQL de executar múltiplas operações em uma única requisição, seja através de queries com aliases ou usando a extensão de batching (como em graphql-batch). Embora útil para performance, o batching pode ser abusado para realizar ataques de força bruta ou exfiltração de dados. Por exemplo, um atacante pode enviar uma query com dezenas de aliases para testar diferentes IDs de usuário simultaneamente:
query {
a: user(id: 1) { name }
b: user(id: 2) { name }
c: user(id: 3) { name }
// ... centenas de aliases
}
Para prevenir abusos, recomenda-se limitar o número de aliases por query (ex.: máximo 10) e implementar rate limiting por IP ou token de autenticação. Além disso, o uso de dataloaders para agrupar carregamentos em lote não deve permitir que o cliente controle o tamanho do lote arbitrariamente. A validação de que o cliente não está repetindo a mesma operação muitas vezes também é uma boa prática.
Autorização
Autorização em GraphQL deve ser aplicada em nível de campo, e não apenas no resolver da query raiz. Um erro comum é verificar se o usuário está autenticado, mas não se ele tem permissão para acessar um campo específico. Por exemplo, um campo email em um tipo User pode ser visível apenas para o próprio usuário ou administradores. Sem autorização granular, um usuário pode consultar dados de outros usuários simplesmente navegando pela relação:
query {
user(id: 2) {
email
posts {
title
content
}
}
}
A solução é implementar uma camada de autorização em cada campo, usando middlewares ou decoradores que verificam políticas (ex.: RBAC, ABAC). Bibliotecas como graphql-shield (Node.js) ou graphql-auth (Python) permitem definir regras por tipo e campo. Além disso, é importante nunca expor IDs internos ou campos de depuração sem verificação. A autorização deve ser testada exaustivamente, incluindo cenários de acesso negado.
Exposição indevida
A exposição indevida ocorre quando informações sensíveis ou não intencionais vazam através do schema GraphQL. Isso inclui campos que revelam detalhes de implementação (como versões de software, chaves de API, senhas hash), mensagens de erro detalhadas que expõem stack traces, e o próprio sistema de introspecção que permite a qualquer cliente descobrir todo o schema. Por exemplo, a query de introspecção abaixo revela todos os tipos, campos e argumentos disponíveis:
query {
__schema {
types {
name
fields {
name
type {
name
}
}
}
}
}
Para mitigar, em produção deve-se desabilitar a introspecção (ou limitá-la a usuários autenticados e autorizados). Mensagens de erro devem ser genéricas (ex.: "Erro interno") e logs detalhados mantidos apenas no servidor. Revise o schema para remover campos desnecessários ou que exponham dados internos (ex.: passwordHash, internalNotes). Use ferramentas de análise de schema para detectar vazamentos potenciais.
Boas práticas e observações finais
Além das mitigações específicas, mantenha o GraphQL atualizado, use HTTPS obrigatório, implemente autenticação forte (JWT, OAuth2) e realize testes de penetração regulares. Considere usar gateways de API que inspecionam e limitam queries GraphQL. Documente as políticas de segurança para a equipe de desenvolvimento.
Referências
- GraphQL Security - Documentação Oficial
- OWASP GraphQL Cheat Sheet
- Apollo Server - Segurança
- graphql-shield - Autorização
- How to GraphQL - Segurança Avançada
Exercícios
- Explique por que queries complexas podem ser um risco de segurança e cite duas formas de mitigação.
- Como o batching pode ser usado em um ataque e qual medida preventiva você recomendaria?
- Qual a diferença entre autenticação e autorização no contexto do GraphQL? Dê um exemplo de falha de autorização.
- Cite duas informações que podem ser expostas indevidamente por um schema GraphQL e como evitá-las.
- Por que desabilitar a introspecção em produção é importante para segurança?
email não tiver verificação de propriedade.
passwordHash) e detalhes de erro (stack traces) podem ser expostos. Para evitar, remova campos sensíveis do schema e use mensagens de erro genéricas em produção.