Voltar ao blog
segurança de APIRESTGraphQLBOLAOAuth2rate limitingOWASP API Security

Segurança de APIs REST e GraphQL: BOLA, autenticação entre serviços e rate limiting

2026-06-16
Segurança de APIs REST e GraphQL: BOLA, autenticação entre serviços e rate limiting

A API virou a espinha dorsal do software moderno. Aplicações web, mobile, integrações de parceiros, microsserviços e agentes de IA conversam entre si por REST e GraphQL o tempo todo. E é justamente aí que mora o problema: enquanto a interface web tem décadas de práticas de defesa consolidadas, a camada de API costuma ser tratada como "encanamento interno", como se estivesse protegida só por estar atrás de um app bonito. Não está.

Ao contrário de uma tela de navegador, uma API expõe diretamente a lógica de negócio e os dados. Não há JavaScript escondendo botão, não há fluxo guiado. Quem chama o endpoint decide exatamente qual objeto pedir, com qual método e qual payload. Por isso a OWASP mantém um Top 10 específico para APIs, separado do Top 10 web tradicional: os padrões de falha são diferentes o suficiente para merecerem um mapa próprio.

Este guia foca nos três eixos que mais derrubam APIs em produção: autorização em nível de objeto (BOLA), autenticação entre serviços e rate limiting, com um olhar extra para os riscos específicos de GraphQL.

BOLA: a falha nº 1 de APIs (e a mais subestimada)

BOLA (Broken Object Level Authorization), também conhecida como IDOR (Insecure Direct Object Reference), é a vulnerabilidade mais comum e mais impactante em APIs. Ela ocupa o topo do OWASP API Security Top 10 por um motivo simples: é trivial de explorar e devastadora de sofrer.

O cenário é banal. Um endpoint retorna os dados de um pedido:

GET /api/orders/1043
Authorization: Bearer <token-do-usuário-A>

A aplicação valida que o token é válido (autenticação ✓) e retorna o pedido 1043. Mas ela verifica se o pedido 1043 pertence ao usuário A? Se a resposta for "não", basta o atacante trocar o ID:

GET /api/orders/1044   → dados de outro cliente
GET /api/orders/1045   → e de outro...

Com um loop de poucas linhas, um usuário legítimo exfiltra a base inteira de pedidos. O erro conceitual é confundir autenticação (quem é você) com autorização (o que você pode acessar). O token prova a identidade; ele não diz nada sobre a propriedade do recurso.

Como prevenir BOLA

Não existe biblioteca mágica: a defesa é disciplina de arquitetura.

  • Autorize por objeto, em cada requisição. Toda vez que um recurso é acessado por ID, verifique no servidor se o sujeito autenticado tem direito àquele objeto específico. WHERE id = ? AND owner_id = ? no banco resolve a maioria dos casos.
  • Prefira IDs não sequenciais e não adivinháveis (UUID, ULID). Isso não é controle de acesso (é defesa em profundidade), mas elimina a enumeração trivial.
  • Centralize a decisão de autorização. Espalhar checagens por dezenas de controladores garante que uma delas será esquecida. Um middleware ou uma camada de política (ex: OPA, Casbin) torna a regra auditável.
  • Negue por padrão. Um novo endpoint deve começar fechado e ser explicitamente liberado, nunca o contrário.

BOLA tem uma prima importante em GraphQL: como um único endpoint resolve grafos inteiros de objetos relacionados, uma falha de autorização em um resolver aninhado (ex: order { customer { creditCard } }) pode vazar dados que o objeto raiz nunca exporia. Autorização em GraphQL precisa acontecer por campo e por nó, não só na entrada da query.

Autenticação entre serviços: o perímetro que não existe mais

Em uma arquitetura de microsserviços, a maior parte do tráfego de API é serviço-para-serviço, não usuário-para-serviço. E aqui um anti-padrão perigoso se repete: assumir que "tráfego interno é confiável". Numa rede plana, um único contêiner comprometido passa a falar com todos os outros sem fricção. O modelo Zero Trust parte do princípio oposto: nenhuma chamada é confiável por origem; toda chamada prova identidade e autorização.

As opções principais para autenticar serviços entre si:

Mecanismo Como funciona Melhor para Cuidado principal
API Key Chave estática no header Integrações simples, parceiros externos Não expira sozinha; rotação e escopo são obrigatórios
OAuth2 Client Credentials Serviço troca client_id/secret por token de curta duração Comunicação entre serviços internos e B2B Guardar o secret com segurança; escopos mínimos
mTLS Ambos os lados apresentam certificado Malha de microsserviços (service mesh) Gestão e rotação de certificados em escala
JWT assinado Token autocontido, validado por assinatura Propagar identidade sem consultar o emissor Validar iss, aud, exp; nunca aceitar alg: none

Na prática, o padrão sólido para serviços internos combina OAuth2 Client Credentials (para obter tokens de vida curta) com mTLS na camada de transporte (para autenticar mutuamente os dois lados). Ferramentas de service mesh (Istio, Linkerd) automatizam o mTLS e a rotação de certificados, tirando esse peso do time de aplicação.

Alguns princípios inegociáveis:

  • Tokens de curta duração. Um token que vale horas é uma janela de horas para quem o roubar. Prefira minutos, com renovação automática.
  • Escopo mínimo. O serviço de faturamento não precisa de um token que também lê a base de RH. Cada credencial carrega só as permissões que usa.
  • Nunca hardcode segredos. Chaves e secrets vivem em um cofre (Vault, AWS Secrets Manager, GCP Secret Manager), injetados em runtime, nunca no código nem no repositório.
  • Valide o JWT de verdade. Cheque assinatura, emissor (iss), audiência (aud) e expiração. Aceitar alg: none ou não validar a audiência são erros clássicos que anulam toda a proteção.

Rate limiting: sua defesa contra abuso, força bruta e custo

Uma API sem rate limiting é uma API sem freio. Ela está exposta a força bruta de credenciais, scraping de dados, negação de serviço por exaustão de recursos e, no caso de APIs que acionam operações caras (envio de e-mail, chamadas a modelos de IA, processamento pesado), a um prejuízo financeiro direto.

Rate limiting responde à pergunta: quantas requisições, de quem, em quanto tempo, eu permito? Os algoritmos mais usados:

  • Token bucket: cada cliente tem um "balde" de fichas que se recarrega a uma taxa fixa. Permite rajadas curtas dentro de um limite médio. É o mais versátil.
  • Fixed window: conta requisições por janela fixa (ex: 100/minuto). Simples, mas sofre com o "efeito de borda" (rajadas na virada da janela).
  • Sliding window: suaviza o efeito de borda contando uma janela deslizante. Mais justo, um pouco mais caro de calcular.

Boas práticas de implementação:

  • Limite por identidade, não só por IP. Atrás de um NAT ou CDN, milhares de usuários compartilham IP. O limite deve ancorar na chave de API, no usuário autenticado ou no cliente OAuth.
  • Responda com 429 Too Many Requests e envie os headers RateLimit-Limit, RateLimit-Remaining e Retry-After, para que clientes bem-comportados saibam se conter.
  • Aplique limites diferenciados por rota. POST /login e POST /password-reset merecem limites muito mais rígidos que uma leitura pública.
  • Camadas complementares: rate limiting na borda (API Gateway, WAF) protege a infraestrutura; limites na aplicação protegem a lógica de negócio. Use ambos.

GraphQL exige defesas extras

GraphQL resolve problemas reais de over/under-fetching, mas transfere poder ao cliente, e poder no cliente é superfície de ataque no servidor. Além de BOLA por resolver, dois riscos são específicos:

  • Query complexity abuse: uma query profundamente aninhada ou com muitos nós pode custar uma explosão de trabalho no backend (o clássico ataque de nested query). Defenda com limite de profundidade (depth limiting), análise de custo/complexidade por query e paginação obrigatória em listas.
  • Introspecção exposta em produção: a introspecção revela todo o schema, um mapa pronto do ataque. Desabilite introspecção e o "GraphiQL" em produção, ou restrinja a ambientes autenticados de desenvolvimento.

Some a isso rate limiting baseado em custo (nem toda query pesa igual), timeouts por operação e allow-listing de queries conhecidas (persisted queries) para APIs internas, e você fecha a maior parte da superfície específica de GraphQL.

Um checklist mínimo antes do deploy

Antes de expor qualquer API à internet, confirme:

  1. Todo acesso a objeto por ID valida propriedade/autorização, não só autenticação.
  2. Autenticação entre serviços usa tokens de curta duração e escopo mínimo; segredos vêm de um cofre.
  3. Existe rate limiting por identidade, com 429 e Retry-After.
  4. Endpoints sensíveis (login, reset, pagamento) têm limites reforçados.
  5. Respostas não fazem over-fetching de dados (sem devolver password_hash, tokens ou campos internos).
  6. Em GraphQL: introspecção desligada em produção, depth/complexity limiting e paginação obrigatória.
  7. Toda a comunicação é sobre TLS; JWTs são validados por assinatura, aud e exp.
  8. Logs de segurança registram tentativas de acesso negado e picos de 429 para detecção.

Segurança de API é engenharia contínua, não um cadeado no fim

A maior parte das brechas de API não vem de criptografia quebrada ou de exploits exóticos: vem de autorização mal feita, credenciais mal geridas e falta de limites. São falhas de projeto e de disciplina, exatamente as que um teste focado e uma boa arquitetura previnem. À medida que o negócio se abre em integrações e microsserviços, cada novo endpoint é uma porta, e cada porta precisa de fechadura, não de esperança.

Na ConstructorTI, tratamos segurança de API como parte da engenharia, não como etapa final. Combinamos pentest de API (BOLA, autenticação entre serviços, abuso de GraphQL), revisão de arquitetura de autorização e desenho de rate limiting e observabilidade, para que sua API cresça exposta ao mundo sem virar o próximo vazamento. Se você opera APIs REST ou GraphQL em produção e quer saber onde estão as portas destrancadas, converse com o nosso time de CyberSecurity.