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.

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:
- Leitura da fatura de outro cliente → dados pessoais (RGPD).
- A fatura traz o UUID do documento → download do PDF com dados bancários.
- O mesmo padrão em
PATCH /api/users/{id}→ troca do e-mail de recuperação → account takeover. - 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_accountvem 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.