O campo que o erro revelou: as sugestões do GraphQL vazam um campo exclusivo da equipe
O desafio
A API GraphQL da Wrenmoor esconde seus campos. Chute um campo: o erro sugere o nome real. Traga o código de override do pedido 1042.
O que você vai aprender
- Explicar o que a introspecção do GraphQL expõe e por que desligá-la não basta
- Usar as sugestões de campo do graphql-js para descobrir nomes de campos que um schema esconde
- Ler as respostas de erro do Apollo Server: código de status, mensagem e código de erro
- Reconhecer a falta de uma checagem de autorização em nível de campo
- Esconder as sugestões em produção e autorizar campos sensíveis
Habilidades testadas
Pré-requisitos
- Como é uma query GraphQL
- Ler uma resposta HTTP em JSON
Como funciona
Uma API GraphQL serve um único schema: todos os tipos e campos que um cliente pode pedir. A introspecção (__schema, __type) permite que qualquer pessoa baixe esse schema, então servidores de produção costumam desligá-la. O Apollo Server faz isso por padrão quando NODE_ENV=production e responde a uma query de introspecção com GraphQL introspection is not allowed by Apollo Server.
Desligar a introspecção não remove o schema. Toda query continua sendo validada contra ele, e o graphql-js responde a um campo desconhecido com os nomes reais mais próximos: Cannot query field "overrideCode" on type "Order". Did you mean "agentOverrideCode"? Um chute errado por vez, o schema oculto volta. O Clairvoyance automatiza isso com uma wordlist, e a Web Security Academy da PortSwigger cobre o assunto no tópico de GraphQL.
Aqui, o nome vazado é um campo do painel de suporte no mesmo tipo Order que a loja usa. O resolver só confere se o pedido pertence a quem faz a chamada, não quem pode ler cada campo, então saber o nome basta para ler o valor. O OWASP API Security Top 10 chama isso de Broken Object Property Level Authorization (API3:2023).
Erros comuns
- Parar no erro de introspecção. Ele só esconde a lista de campos; a validação de campos continua falando.
- Chutar no tipo errado.
{ agentOverrideCode }no nível superior pergunta ao tipoQuery, que não tem esse campo. Peça dentro deorder(id: 1042) { ... }. - Quebrar o corpo JSON. Uma aspa dupla dentro da query, como em
__type(name: "Order"), precisa ser escrita\"; senão o Express rejeita o corpo com uma página 400 antes de o GraphQL ver a requisição. - Tentar outro pedido.
order(id: 1041)é recusado: a checagem do objeto funciona. O vazamento é um campo do seu próprio pedido. - Responder com o nome do campo, ou com o código antigo. A resposta é o valor de
agentOverrideCode; oOVR-TEST-1188emagentNoteexpirou.
Como se proteger
Feche o vazamento e adicione a checagem que faltava.
- Esconda as sugestões em produção:
new ApolloServer({ hideSchemaDetailsFromClientErrors: true })(Apollo Server 4.11 e posterior), ou o plugin block-field-suggestions do graphql-armor em outros servidores. - Autorize campos, não só objetos: dê a cada campo exclusivo da equipe a sua própria checagem de papel no resolver (ou uma diretiva de autorização), ou sirva o painel de suporte a partir de um schema separado.
- Trate o schema como público de qualquer forma: introspecção desligada e sugestões escondidas atrasam um atacante, mas não protegem dados. Para os seus próprios clientes, uma allowlist de persisted queries impede queries arbitrárias de vez.