APIs REST são fundamentais para a comunicação entre sistemas modernos, mas sua exposição pública as torna alvos frequentes de ataques. Garantir a segurança de uma API envolve múltiplas camadas de proteção, desde a autenticação de usuários até o controle de acesso e a proteção contra abusos. Nesta aula, exploraremos quatro pilares críticos: autenticação, rate limiting, validação de entrada e gerenciamento de segredos.

Cada um desses tópicos desempenha um papel vital na defesa contra ameaças comuns, como ataques de força bruta, injeção de dados, exaustão de recursos e vazamento de informações sensíveis. Ao final, você terá um guia prático para implementar essas medidas em suas próprias APIs.

Autenticação

Autenticação é o processo de verificar a identidade de um cliente que faz uma requisição à API. Sem ela, qualquer pessoa poderia acessar recursos protegidos. Os métodos mais comuns incluem tokens JWT (JSON Web Tokens), chaves de API e OAuth 2.0. O JWT é amplamente utilizado por ser stateless: o servidor não precisa armazenar sessões, pois as informações do usuário e a assinatura digital estão contidas no token.

Para implementar autenticação segura, sempre utilize HTTPS para evitar interceptação de credenciais. Armazene tokens de forma segura no cliente (por exemplo, em cookies httpOnly ou em memória) e nunca exponha segredos em URLs ou logs. Além disso, estabeleça um tempo de expiração curto para tokens e implemente refresh tokens para renovação segura.

Exemplo de fluxo de autenticação com JWT:

1. Cliente envia credenciais (usuário/senha) para /api/login.
2. Servidor valida as credenciais e gera um JWT com payload (user_id, role, exp).
3. Servidor retorna o JWT no corpo da resposta ou em um cookie seguro.
4. Cliente envia o JWT no header Authorization: Bearer <token> em requisições subsequentes.
5. Servidor verifica a assinatura e a expiração do JWT antes de processar a requisição.

Boas práticas: use algoritmos de assinatura fortes (HS256 ou RS256), evite incluir informações sensíveis no payload e sempre valide a emissão (iss) e audiência (aud) do token.

Rate Limiting

Rate limiting controla o número de requisições que um cliente pode fazer em um determinado período. Isso protege a API contra abusos, como ataques de força bruta, DDoS e uso excessivo que pode sobrecarregar o servidor. Estratégias comuns incluem limite por IP, por token de autenticação ou por chave de API.

Implementações típicas usam algoritmos como Token Bucket, Leaky Bucket ou Sliding Window. O servidor retorna códigos de status HTTP 429 (Too Many Requests) quando o limite é excedido, juntamente com headers informativos como Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset.

Exemplo de configuração de rate limiting em um gateway de API (pseudo-código):

# Configuração: 100 requisições por minuto por usuário
rate_limit = RateLimit(requests=100, window=60, key='user_id')

# Middleware de rate limiting
app.use(rate_limit.middleware)

# Rota protegida
@app.route('/api/recurso')
def recurso():
    return {'dados': 'exemplo'}

Boas práticas: defina limites diferentes para endpoints críticos (ex.: login) versus endpoints de leitura. Use cache distribuído (Redis) para rate limiting em ambientes escaláveis. Sempre informe o cliente sobre o limite e o tempo de reset.

Input Validation

Validação de entrada é a primeira linha de defesa contra ataques de injeção (SQL, NoSQL, OS command, etc.) e dados malformados. Nunca confie em dados fornecidos pelo cliente: todo input deve ser verificado quanto ao tipo, formato, tamanho e conteúdo esperado. Utilize bibliotecas de validação robustas e evite construir consultas ou comandos concatenando strings.

Exemplo de validação de entrada em uma API REST (pseudo-código):

# Validação de campo 'email' em requisição POST
import re

def validate_email(email):
    if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
        raise ValidationError('Email inválido')
    if len(email) > 254:
        raise ValidationError('Email muito longo')
    return email

Além de validação, use prepared statements ou ORMs para consultas a bancos de dados e escape adequado para saídas. Nunca exponha detalhes internos do sistema em mensagens de erro: retorne respostas genéricas como "Dados inválidos" e registre logs detalhados internamente.

Secrets Management

Gerenciamento de segredos envolve proteger informações sensíveis como chaves de API, senhas de banco de dados, tokens de autenticação e certificados. Nunca hardcode segredos no código-fonte ou em arquivos de configuração versionados. Utilize ferramentas como cofres de segredos (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) ou variáveis de ambiente criptografadas.

Boas práticas: rotacione segredos regularmente, restrinja o acesso com base no princípio do menor privilégio, e use criptografia em repouso e em trânsito. Em ambientes de desenvolvimento, use segredos mock ou locais, mas nunca compartilhe segredos de produção em repositórios.

Exemplo de uso de variáveis de ambiente para segredos (pseudo-código):

import os

DB_PASSWORD = os.environ.get('DB_PASSWORD')
if not DB_PASSWORD:
    raise Exception('DB_PASSWORD não definido')

# Conectar ao banco com a senha segura

Para ambientes em contêiner, evite passar segredos como variáveis de ambiente se o sistema de orquestração não for seguro; prefira montar volumes com segredos ou usar sidecars dedicados.

Boas Práticas e Observações Finais

Além dos pilares abordados, considere implementar logging e monitoramento de segurança, usar firewalls de API (WAF) e realizar testes de penetração regularmente. A segurança é um processo contínuo: mantenha-se atualizado sobre novas vulnerabilidades e atualize suas dependências. Documente claramente as políticas de segurança para consumidores da API, incluindo limites de taxa e métodos de autenticação suportados.

Referências

Exercícios

  1. Explique por que é importante usar HTTPS em APIs que implementam autenticação via JWT.
  2. ✓ Resposta: HTTPS criptografa a comunicação entre cliente e servidor, impedindo que um atacante intercepte o token JWT durante a transmissão. Sem HTTPS, o token pode ser capturado em ataques man-in-the-middle, comprometendo a autenticação.
  3. Descreva como o rate limiting pode ajudar a prevenir ataques de força bruta em endpoints de login.
  4. ✓ Resposta: Rate limiting restringe o número de tentativas de login que um mesmo IP ou usuário pode fazer em um intervalo de tempo. Isso dificulta que um atacante tente milhares de senhas rapidamente, pois exceder o limite resulta em bloqueio temporário (HTTP 429).
  5. Cite três tipos de ataque que a validação de entrada pode prevenir e dê um exemplo de cada.
  6. ✓ Resposta: 1) SQL Injection: entrada como "'; DROP TABLE usuarios;--" pode ser evitada com validação de tipos e uso de prepared statements. 2) Cross-Site Scripting (XSS): entrada como "" deve ser escapada ou rejeitada. 3) Command Injection: entrada como "; rm -rf /" deve ser validada para não conter caracteres especiais não permitidos.
  7. Por que não se deve hardcodear segredos no código-fonte? Mencione pelo menos duas alternativas seguras.
  8. ✓ Resposta: Hardcodear segredos expõe informações sensíveis se o código for acessado por pessoas não autorizadas, como em repositórios públicos ou vazados. Alternativas seguras: usar variáveis de ambiente (ex.: process.env.DB_PASSWORD) ou um cofre de segredos como HashiCorp Vault, que fornece acesso controlado e auditoria.
  9. Projete um cenário onde a falta de rate limiting em uma API pública pode levar a um ataque de negação de serviço (DoS). Como você mitigaria?
  10. ✓ Resposta: Um atacante pode enviar um grande volume de requisições a um endpoint caro (ex.: que consulta banco de dados) sem rate limiting, sobrecarregando o servidor e tornando a API indisponível para usuários legítimos. Mitigação: implementar rate limiting por IP e por token, com limites baixos para endpoints críticos, e usar um Web Application Firewall (WAF) para detectar padrões anômalos.