API 5 min de lectura

BOLA em APIs: porque continua a ser o achado n.º 1 e como o encadeamos até dados de outros clientes

Broken Object Level Authorization lidera o OWASP API Top 10 há anos e continua a ser o achado crítico mais comum nos nossos pentests de API. O mecanismo, as variantes que os scanners não apanham e o que corrige mesmo.

También en PTENPT-BR
BOLA em APIs: porque continua a ser o achado n.º 1 e como o encadeamos até dados de outros clientes

Um número no sítio errado

GET /api/v2/invoices/48213 devolve a fatura do utilizador autenticado. GET /api/v2/invoices/48212 devolve a fatura de outro cliente. Sem erro, sem log, sem alerta. Isto é BOLA — Broken Object Level Authorization —, o item API1 do OWASP API Security Top 10, o mesmo problema a que o mundo web chamava IDOR.

É o achado crítico mais frequente nos nossos pentests de API, e não por descuido das equipas: nasce de uma decisão de arquitetura razoável (identificar objetos por ID) combinada com uma verificação que parece existir mas está no sítio errado.

Porque é que os scanners não o encontram

Um scanner não sabe o que é "seu" e o que é "do outro". Vê duas respostas 200 com JSON válido e segue em frente. Detetar BOLA exige dois contextos autenticados, compreensão do modelo de dados e a pergunta certa: este utilizador devia ver isto? Isso é trabalho manual — ou tooling próprio que transporta o contexto de identidade, que é como fazemos.

As variantes que mais encontramos

1. ID sequencial ou previsível

O caso clássico. Inteiro incremental, timestamp, NIF, e-mail no URL. Enumerável em minutos.

2. O UUID que se revela noutro sítio

"Usamos UUID v4, não se adivinha." Verdade — mas o UUID do cliente aparece na resposta de /api/orders, no link do e-mail de confirmação, no HTML da página pública, no webhook para o parceiro. Obscuridade não é autorização.

3. Autorização só no GET, não no PATCH

A equipa protegeu a leitura. A escrita (PUT, PATCH, DELETE) usa outro handler, escrito depois, sem o mesmo middleware. Ler é mau; alterar a morada de entrega ou o IBAN de outro cliente é pior.

4. O ID no corpo, não no URL

POST /api/transfer com {"from_account": "...", "to_account": "..."}. O backend confia no from_account enviado pelo cliente em vez de o derivar da sessão. Já vimos isto numa fintech regulada.

5. GraphQL e batching

query { user(id: 48212) { email, documents { url } } }. Resolvers aninhados verificam frequentemente a autorização apenas no nó raiz. Com aliases e batching, enumeram-se milhares de objetos num único pedido.

6. Endpoints "internos" ou legados

/api/v1/ ficou no ar depois da /v2/. Ou /internal/export sem autenticação porque "só o frontend de admin o chama". O frontend não é fronteira de segurança.

7. Autorização por tenant, não por objeto

Em SaaS multi-tenant, verifica-se se o utilizador pertence à empresa X — mas não se o objeto pertence à empresa X. Um utilizador de X lê objetos de Y trocando o ID.

Como encadeamos: do 200 ao impacto no negócio

Um BOLA isolado é grave. O que o relatório tem de mostrar é o caminho até ao dano real:

  1. Leitura da fatura de outro cliente → dados pessoais (RGPD).
  2. A fatura traz o UUID do documento → download do PDF com dados bancários.
  3. O mesmo padrão em PATCH /api/users/{id} → troca do e-mail de recuperação → account takeover.
  4. Com a conta, acesso a POST /api/payouts → movimentação financeira.

Cada passo é um pedido documentado com request/response no relatório. A administração não precisa de perceber IDOR; precisa de perceber "qualquer utilizador conseguia mudar o IBAN de qualquer outro".

O que corrige a sério

  • Derive a identidade da sessão, nunca do input. O from_account vem do token, não do JSON.
  • Autorize no objeto, em todas as operações. Um único ponto (policy, middleware, repositório) que responde "este sujeito pode fazer esta ação neste objeto?" — e todos os handlers passam por ele, incluindo resolvers de GraphQL e jobs internos.
  • Delimite na consulta, não depois. WHERE id = ? AND owner_id = ? na base de dados. Filtrar em memória depois de ir buscar tudo é um convite à fuga.
  • Testes automáticos de autorização na CI: para cada endpoint, dois utilizadores, um objeto de cada, asserção de 403/404. Evita regressões; não substitui o pentest, que encontra os caminhos para os quais ninguém escreveu teste.
  • Rate limit e alertas em padrões de enumeração — não impede, mas encurta a janela.
  • Retire o legado. Versões antigas da API precisam de data de desligamento, não de esquecimento.

O que pedir no seu próximo pentest de API

Exija que o teste seja feito com pelo menos dois utilizadores por papel e com o OpenAPI/Swagger em mãos (grey-box). Peça cobertura explícita de BOLA, BFLA (autorização ao nível da função, API5), mass assignment (API3) e dos endpoints não documentados que o recon encontrar. E peça o encadeamento: um relatório com 30 IDOR isolados vale menos do que um com o caminho até ao takeover.

Como a Pentest Machine testa isto

O nosso API Pentest é guiado pelo OWASP API Top 10 e pela especificação da sua API, com múltiplas identidades por papel e tooling próprio que transporta contexto de autorização durante a enumeração — o que permite testar milhares de combinações objeto × utilizador × operação sem perder a análise manual das cadeias. Cada achado inclui PoC reproduzível, impacto no negócio e o padrão de correção, e o reteste está incluído.