Guia · APIs
Vulnerabilidades comuns em APIs REST (e como corrigir)
9 min de leitura · atualizado em 12 de julho de 2026
Por que APIs viraram o alvo nº 1
Todo produto moderno é, no fundo, uma API: o app mobile, o SPA no navegador e as integrações de parceiros conversam com o mesmo backend HTTP. Isso muda a superfície de ataque. A tela some, mas o endpoint continua lá — e um atacante fala direto com ele, sem passar pelo Javascript que você achava que era a barreira.
Por isso o OWASP mantém uma lista separada só para isso: o OWASP API Security Top 10 (edição 2023). Ela existe porque as falhas de API têm uma cara própria — quase sempre são erros de autorização e de confiar no que o cliente manda, não os clássicos XSS/CSRF de página web. As seções abaixo seguem essa lista, da falha mais comum para a mais sutil.
BOLA / IDOR — autorização quebrada em nível de objeto
É a falha nº 1 de APIs (API1:2023). Acontece quando o endpoint checa que você está logado, mas não checa se aquele objeto específico é seu. O ID vem na URL e o backend confia nele.
Exemplo: GET /api/invoices/1043 devolve a fatura 1043. Você troca para GET /api/invoices/1044 e recebe a fatura de outro cliente. Nenhum erro, nenhum bloqueio — só o dado de outra pessoa. Isso é IDOR (Insecure Direct Object Reference), a forma prática do BOLA.
Como corrigir: em toda leitura ou escrita de objeto, valide a posse no servidor — WHERE id = :id AND owner_id = :usuario_atual — em vez de só WHERE id = :id. Nunca derive a autorização do que o cliente enviou. IDs sequenciais previsíveis pioram o quadro; UUIDs ajudam a ofuscar, mas não substituem a checagem de posse.
Broken Authentication — autenticação frágil
API2:2023. Aqui o problema é como você prova quem é o usuário. Os casos clássicos: JWT mal validado (o servidor aceita o token sem conferir a assinatura, ou aceita alg: none), tokens sem expiração (um token vazado vale para sempre), endpoints de login sem proteção contra força bruta e recuperação de senha que vaza se um e-mail existe.
Exemplo: um Authorization: Bearer <jwt> em que o backend faz jwt.decode() mas nunca jwt.verify() — qualquer um forja o payload {"role":"admin"} e entra como admin.
Como corrigir: verifique a assinatura sempre, com o algoritmo fixado no servidor (nunca leia alg do próprio token). Dê expiração curta ao access token e use refresh token revogável. Rate limit e bloqueio progressivo no login. E trate segredos de assinatura como segredos: fora do repositório, rotacionáveis.
Excessive Data Exposure — a API devolve campos demais
Faz parte de API3:2023 (Broken Object Property Level Authorization) na edição 2023. O backend retorna o objeto inteiro do banco e confia que o front vai mostrar só o que interessa. Só que o dado trafega igual — basta abrir o DevTools ou chamar o endpoint direto.
Exemplo: GET /api/users/me devolve { id, nome, email, senha_hash, cpf, is_admin, stripe_customer_id }. A tela usa só nome e e-mail, mas o hash de senha e o CPF foram parar na resposta.
Como corrigir: nunca serialize a entidade crua. Defina um DTO/schema de saída explícito que lista só os campos permitidos (allowlist), em vez de remover campos sensíveis um a um (blocklist — sempre esquece um). Filtragem de propriedade é responsabilidade do servidor, não do cliente.
Mass Assignment — o cliente escreve campos que não devia
O outro lado de API3:2023: exposição é o servidor devolvendo demais; mass assignment é o servidor aceitando demais. Acontece quando você joga o corpo da requisição direto no objeto do banco.
Exemplo: o cadastro espera { nome, email }, mas o atacante manda { nome, email, is_admin: true }. Se o código faz db.users.update(id, req.body), ele acabou de se promover a admin. O mesmo vale para saldo, plano, owner_id.
Como corrigir: aceite apenas uma lista explícita de campos por endpoint (allowlist de input). Valide o corpo contra um schema estrito que rejeita propriedades desconhecidas. Campos sensíveis — papel, saldo, dono — só mudam por fluxos dedicados e autorizados, nunca pelo update genérico.
Unrestricted Resource Consumption — falta de rate limiting
API4:2023. Sem limite de requisições, um único cliente derruba (ou esvazia o caixa) da sua API. Vale para força bruta em login, envio em massa de OTP/e-mail (que custam dinheiro), e queries pesadas que consomem CPU e banco.
Exemplo: POST /api/login aceita 10.000 tentativas por minuto do mesmo IP sem se importar. Ou GET /api/reports?limit=1000000, em que o cliente escolhe um tamanho de página que trava o servidor.
Como corrigir: rate limiting por IP, por usuário e por endpoint sensível. Teto máximo de paginação imposto no servidor (não confie no limit do cliente). Timeouts e limites de payload. Para operações que custam dinheiro (SMS, e-mail, IA), cotas e circuit breakers.
BFLA — autorização quebrada em nível de função
API5:2023 (Broken Function Level Authorization). Se BOLA é acessar o objeto de outra pessoa, BFLA é acessar uma função que não é do seu nível — tipicamente uma ação de admin. O endpoint existe, some da UI para usuários comuns, mas continua respondendo se você o chamar direto.
Exemplo: a interface só mostra o botão "excluir usuário" para admins, mas DELETE /api/admin/users/55 funciona para qualquer conta autenticada, porque o backend nunca checou o papel. Vale também para trocar de método: GET é público, mas o PUT no mesmo recurso deveria ser restrito e não é.
Como corrigir: autorização por papel/permissão em todo endpoint administrativo ou privilegiado, aplicada por padrão (deny by default). Não dependa de esconder a rota — ela precisa recusar a chamada, não só sumir do menu.
Resumo: falha → correção
| Falha (OWASP API 2023) | Sintoma | Correção |
|---|---|---|
| BOLA / IDOR (API1) | Troca o ID na URL e vê dado alheio | Checar posse no servidor: owner_id = usuário atual |
| Broken Authentication (API2) | JWT sem verificar assinatura, token eterno | Verificar assinatura, alg fixo, expiração curta + refresh |
| Excessive Data Exposure (API3) | Resposta traz senha_hash, CPF, is_admin | DTO de saída com allowlist de campos |
| Mass Assignment (API3) | Body extra vira is_admin: true | Allowlist de input, schema estrito |
| Resource Consumption (API4) | Força bruta, paginação gigante | Rate limit + teto de paginação no servidor |
| BFLA (API5) | Rota de admin responde a usuário comum | Autorização por papel, deny by default |
Por que scanner genérico erra em API
As três falhas mais comuns de API — BOLA, BFLA e mass assignment — são falhas de autorização. E autorização é contexto: para saber que a fatura 1044 não é sua, a ferramenta precisa estar autenticada como você, entender que existe um dono, e tentar acessar o objeto de outro. Um scanner de vulnerabilidade genérico não tem esse contexto: ele bate no endpoint anônimo, casa assinaturas de CVE e devolve uma lista de possibilidades — e passa reto por um IDOR que só aparece dentro da área logada.
O Escudo Code ataca justamente essa lacuna. Ele compila a especificação OpenAPI e as rotas da sua API, autentica na área logada e explora encadeamentos — troca IDs, tenta escalar de função, força campos extras — e só reporta o que confirmou, com evidência e severidade CVSS. É a diferença entre "esse endpoint pode ter IDOR" e "acessei a fatura 1044 que não é sua; aqui está a requisição". Validamos essa capacidade contra a VAmPI, uma API propositalmente vulnerável, exatamente porque o desafio dela é autorização, não CVE de biblioteca.
Quer isso rodando de forma contínua? Veja como testar seu SaaS a cada deploy e o OWASP Top 10 explicado para o pano de fundo das categorias.
Perguntas frequentes
Por que um scanner comum não acha IDOR?+–
Porque IDOR é uma falha de autorização, que depende de contexto. O scanner bate no endpoint sem entender que existe um dono do objeto e sem estar autenticado como um usuário real tentando acessar o dado de outro. Ele casa assinaturas de CVE conhecidas; um IDOR não é uma CVE, é uma regra de negócio quebrada que só aparece dentro da área logada.
JWT é seguro?+–
JWT é seguro quando usado direito, e uma bomba quando não. Os erros comuns são não verificar a assinatura (só decodificar), aceitar o algoritmo none, não fixar o algoritmo no servidor e emitir tokens sem expiração. Verifique sempre a assinatura com alg fixo, use expiração curta com refresh token revogável e trate o segredo de assinatura como segredo.
GraphQL tem os mesmos problemas?+–
Sim, e alguns amplificados. BOLA, BFLA, mass assignment e exposição de dados existem igual em GraphQL — a diferença é que a flexibilidade das queries facilita pedir campos demais e montar consultas caras, então rate limiting e limite de profundidade/complexidade ficam ainda mais importantes. As correções de fundo são as mesmas: autorização por objeto e por função no servidor.
Basta usar UUID em vez de ID sequencial para evitar IDOR?+–
Não. UUID ofusca o próximo ID e dificulta a adivinhação, mas não é autorização. Se o endpoint devolve o objeto para qualquer um que informe o UUID certo — e UUIDs vazam em logs, URLs e respostas — a falha continua. A correção real é checar a posse do objeto no servidor a cada acesso.
Teste o seu site agora — a partir de R$50.
Só a URL. Pentest automatizado que ataca de verdade, ao vivo, com relatório do que corrigir.
Rodar o primeiro pentest