📬 Toda semana, um resumo de segurança, Clean Code e IA no seu e-mail Inscreva-se →

Segurança de API: como proteger a comunicação entre sistemas

Você sobe uma API nova, testa no Postman, tudo funciona, vai pra produção. Duas semanas depois alguém percebe que um endpoint de consulta de usuário está devolvendo o objeto inteiro do banco — incluindo hash de senha e campos internos que nunca deveriam sair da API. Ninguém “invadiu” nada. A API simplesmente devolvia mais do que devia, e alguém percebeu isso olhando a resposta no DevTools do navegador.

Esse tipo de falha é mais comum do que parece, e é exatamente o motivo de segurança de API não ser um “extra” — é parte do contrato da API funcionar.

Por que API virou ponto crítico

Uma API é, na prática, uma interface padronizada que permite que sistemas diferentes conversem sem que um precise entender os detalhes internos do outro. Um app de previsão do tempo, por exemplo, agrega dados de temperatura, vento e chuva de fontes distintas e entrega isso de forma consistente pro usuário final — sem que o app precise saber como cada fonte armazena os dados internamente.

O problema é que essa mesma característica que torna a API útil — expor funcionalidade de forma simples — é também o que a torna alvo. Se a API não está bem protegida, uma chamada maliciosa pode acessar dado sensível ou até derrubar o serviço.

Tipos de API (rapidinho, pra contextualizar)

  • REST — a mais usada hoje, simples e compatível com o ecossistema web.
  • SOAP — mais rígida e verbosa, ainda comum em ambiente corporativo legado.
  • RPC e WebSocket — voltadas para comunicação em tempo real ou chamadas de procedimento remoto.

Independente do tipo, o risco de segurança é o mesmo: toda API que expõe dado ou funcionalidade é uma superfície de ataque.

Os riscos que mais aparecem

Exposição excessiva de dados. É o exemplo do início deste artigo — a API devolve mais campos do que o cliente precisa, e algum desses campos é sensível. A causa raiz quase sempre é a mesma: o desenvolvedor serializou o objeto do banco direto na resposta, em vez de montar um DTO explícito com só os campos que devem sair.

Brute force e ataques de negação de serviço. Sem controle de volume de requisições, um script malicioso — ou até um cliente mal escrito do seu próprio time — consegue sobrecarregar o servidor e derrubar o serviço para todo mundo.

O que realmente protege sua API

1. Autenticação e autorização

Autenticação prova quem está fazendo a chamada; autorização define o que essa identidade pode fazer. São coisas diferentes e é comum ver sistemas que implementam uma e esquecem da outra — a API autentica o usuário certinho, mas não checa se aquele usuário tem permissão para acessar aquele recurso específico (esse é, inclusive, um dos problemas mais comuns em API real: BOLA, Broken Object Level Authorization, onde o usuário A consegue acessar o recurso do usuário B só trocando um ID na URL).

  • OAuth2 — mais complexo de implementar, mas é o padrão de mercado para delegação de acesso com bom nível de segurança.
  • API keys e tokens de acesso — mais simples, funcionam bem para cenários onde você controla os dois lados da integração.

2. Validação e sanitização de dados

Todo dado que entra na API precisa ser tratado como não confiável até prova em contrário. Use listas de valores aceitos, valide tipo e formato, e nunca assuma que o cliente vai mandar exatamente o que você espera.

3. Criptografia

Dado em trânsito e dado em repouso são dois problemas diferentes. Para trânsito, TLS não é opcional. Para dado armazenado, avalie criptografia simétrica para o que precisa ser lido de volta pela aplicação, e hashing (nunca criptografia reversível) para senha.

4. Rate limiting

Limitar quantas requisições um cliente pode fazer num intervalo de tempo é a defesa mais direta contra DoS e brute force. Não é sofisticado, mas resolve um problema real e é barato de implementar.

5. Tratamento de erro e logging

Aqui mora uma armadilha comum: seu log interno precisa ser detalhado o suficiente para você debugar, mas a mensagem de erro que volta pro cliente não pode vazar detalhe de implementação. Um erro 500 que devolve stack trace completo pro cliente está entregando de bandeja informação que facilita um ataque.

Um exemplo simples do problema de exposição excessiva, e como corrigir:

# Errado: serializa o objeto inteiro do banco
@app.get("/users/{id}")
def get_user(id):
    return db.get_user(id)  # inclui password_hash, campos internos, tudo

# Certo: DTO explícito, só o que o cliente precisa
@app.get("/users/{id}")
def get_user(id):
    user = db.get_user(id)
    return {"id": user.id, "name": user.name, "email": user.email}

Parece bobo, mas esse único padrão — nunca serializar o modelo de banco direto na resposta — já elimina boa parte dos incidentes de exposição de dados que aparecem em API real.

Fechando

API é infraestrutura crítica, não um detalhe de implementação. As práticas acima não são uma lista de “seria bom fazer” — são o mínimo para uma API que vai receber tráfego de produção. Se você está construindo ou revisando uma API agora, comece pela autenticação/autorização e pela validação de entrada: são os dois pontos onde a maioria das falhas graves de fato mora.

Se você já passou por um incidente de API na pele — exposição de dado, brute force, o que for — conta nos comentários. Esse tipo de relato real vale mais que qualquer checklist genérico.

Leandro Rosendo Candido

Leandro Rosendo Candido

Engenheiro de Computação, mestre pela USP, 10+ anos com Embedded Linux. Escreve e grava sobre segurança, Clean Code e IA aplicada à programação.

Rolar para cima