Autenticação em microserviços

1. Fundamentos da Autenticação Distribuída

1.1. Desafios de segurança em arquiteturas de microserviços

Em uma arquitetura monolítica, a autenticação é trivial: uma sessão compartilhada em memória ou banco de dados resolve o problema. Em microserviços, cada serviço pode rodar em processos, máquinas ou até datacenters diferentes. Isso introduz três desafios críticos:

  • Ausência de sessão compartilhada: não é possível simplesmente consultar uma sessão HTTP centralizada sem criar um ponto único de falha.
  • Latência: cada requisição de autenticação a um serviço central aumenta o tempo de resposta.
  • Pontos únicos de falha: um serviço de autenticação que cai pode derrubar todo o ecossistema.

1.2. Diferenças entre autenticação monolítica vs. distribuída

Aspecto Monolítico Microserviços
Estado da sessão Memória local Stateless (JWT) ou cache distribuído
Validação Por sessão Por token assinado
Escopo Um único contexto Múltiplos contextos e domínios

1.3. Princípios de confiança zero (Zero Trust) aplicados à autenticação

O modelo Zero Trust parte da premissa "nunca confie, sempre verifique". Em microserviços, isso significa:

  • Todo token deve ser validado criptograficamente a cada requisição.
  • Serviços internos não confiam uns nos outros por padrão.
  • A identidade do usuário deve ser propagada com claims verificáveis.

Exemplo de validação mínima em um serviço:

# Pseudocódigo: validação de JWT em middleware
function validateToken(token):
    if not token:
        return 401
    try:
        payload = jwt.decode(token, public_key, algorithms=['RS256'])
        if payload.exp < now():
            return 401
        return payload
    except:
        return 401

2. Padrão JWT (JSON Web Tokens)

2.1. Estrutura do token: header, payload, assinatura – e riscos de exposição de dados

Um JWT é composto por três partes codificadas em Base64Url:

header: {"alg": "RS256", "typ": "JWT"}
payload: {"sub": "user123", "role": "admin", "exp": 1718000000}
signature: [assinatura RSA do header + payload]

Risco: o payload não é criptografado, apenas assinado. Qualquer dado sensível (CPF, senha) no payload fica visível para quem interceptar o token. Use apenas claims não sensíveis.

2.2. Ciclo de vida, refresh tokens e revogação

JWTs geralmente têm expiração curta (15-30 minutos). Para sessões longas, usa-se um refresh token:

# Fluxo de refresh
POST /auth/refresh
Body: { "refresh_token": "rt_abc123" }
Response: { "access_token": "jwt_novo", "expires_in": 900 }

Revogação: como JWTs são stateless, não há como invalidá-los individualmente sem uma blacklist. Estratégias comuns:
- Blacklist em Redis (trade-off: estado centralizado)
- Tempo de expiração curto (aceita o risco de tokens roubados por minutos)

2.3. Boas práticas de armazenamento e transmissão

  • Nunca envie tokens em URLs (query string) — ficam em logs de servidor.
  • Prefira o header Authorization: Bearer <token>.
  • Em aplicações web, use cookies HttpOnly e Secure com atributo SameSite=Strict.
# Exemplo de cookie seguro
Set-Cookie: access_token=jwt_value; HttpOnly; Secure; SameSite=Strict; Path=/api

3. API Gateway como Gatekeeper de Autenticação

3.1. Centralização da validação de tokens no gateway

O API Gateway valida o JWT uma única vez e encaminha as claims para os serviços internos via headers internos:

# Configuração de gateway (exemplo em Kong)
plugins:
  - name: jwt
    config:
      key_claim_name: "iss"
      secret_is_base64: false
      run_on_preflight: true

3.2. Encaminhamento de claims e headers internos

O gateway adiciona headers como X-User-Id e X-User-Role. Cuidado: serviços internos nunca devem confiar em headers que chegam de fora do cluster. O gateway deve remover headers suspeitos antes de encaminhar.

# Remoção de headers externos no gateway
request.set_header("X-User-Id", jwt_payload.sub)
request.remove_header("X-Original-User")  # possível spoofing

3.3. Rate limiting e proteção contra ataques de força bruta

O endpoint de login é o alvo principal. Configure rate limiting no gateway:

# Configuração de rate limit (exemplo em NGINX)
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
location /auth/login {
    limit_req zone=login burst=3 nodelay;
    proxy_pass http://auth-service;
}

4. Comunicação Interna entre Serviços

4.1. Autenticação serviço-a-serviço

Três abordagens comuns:

  • Tokens de serviço: JWTs emitidos para o serviço, com claims como service_name e permissions.
  • mTLS: certificados mútuos TLS — cada serviço apresenta um certificado assinado por uma CA interna.
  • Chaves de API: compartilhadas entre serviços (menos seguro, mas simples).
# Exemplo de token de serviço
header: {"alg": "RS256"}
payload: {"sub": "payment-service", "scope": "read:orders", "exp": 1718000000}

4.2. Propagação de identidade do usuário

Para preservar o contexto do usuário em chamadas internas, propague o JWT original ou um token de contexto interno:

# Propagação segura
service_a -> service_b:
  Header: Authorization: Bearer <original_jwt>
  # Ou header interno: X-User-Context: {"id": "123", "role": "admin"}

4.3. Riscos de confiança cega entre serviços internos

Nunca assuma que um serviço interno é confiável. Um atacante que comprometa um serviço pode se passar por outro. Mitigações:

  • Valide tokens de serviço mesmo em chamadas internas.
  • Use mTLS para garantir que apenas serviços autorizados se comuniquem.
  • Implemente políticas de rede (service mesh, firewalls internos).

5. Single Sign-On (SSO) e Federação de Identidade

5.1. Integração com provedores OAuth 2.0 / OpenID Connect

Um Identity Provider (IdP) externo como Keycloak, Auth0 ou Okta gerencia a autenticação:

# Fluxo OAuth 2.0 Authorization Code
1. Usuário acessa /login
2. Redireciona para IdP: https://idp.example.com/auth?client_id=app&redirect_uri=/callback
3. IdP autentica e redireciona com código: /callback?code=abc
4. Servidor troca código por tokens: POST /token { code, client_secret }
5. Recebe access_token + refresh_token + id_token (OpenID Connect)

5.2. Fluxo de autorização: qual escolher?

Fluxo Uso recomendado Segurança
Authorization Code Aplicações server-side Alto (código + client_secret)
Implicit Legado (evitar) Baixo (token na URL)
Client Credentials Serviço-a-serviço Médio (sem usuário)

5.3. Gerenciamento de sessões distribuídas e logout global

Para logout global (Single Logout), o IdP precisa notificar todos os serviços. Estratégias:

  • Usar um webhook do IdP que invalida tokens em uma blacklist compartilhada.
  • JWTs com expiração curta (logout efetivo em minutos).
# Exemplo de endpoint de logout no gateway
POST /logout
Header: Authorization: Bearer <token>
Response: 200 + invalidação na blacklist Redis

6. Segurança no Armazenamento e Gerenciamento de Segredos

6.1. HS256 vs. RS256

  • HS256 (simétrico): mesma chave para assinar e verificar. Se a chave vazar, qualquer um pode forjar tokens.
  • RS256 (assimétrico): chave privada para assinar, pública para verificar. Serviços só precisam da chave pública.

Recomendação: prefira RS256. A chave privada fica apenas no emissor.

6.2. Uso de cofres de segredos

Nunca hardcode chaves de assinatura. Use ferramentas como HashiCorp Vault ou AWS Secrets Manager:

# Exemplo com Vault (CLI)
vault write auth/jwt/role/my-role allowed_redirect_uris="https://app.example.com/callback"
vault read auth/jwt/config

6.3. Proteção contra vazamento de tokens em logs

Tokens em logs são um risco grave. Configure filtros de log:

# Exemplo em Python com logging
import logging
class TokenFilter(logging.Filter):
    def filter(self, record):
        record.msg = record.msg.replace(r'Bearer\s+\S+', 'Bearer [REDACTED]')
        return True

logger.addFilter(TokenFilter())

7. Monitoramento e Detecção de Ameaças

7.1. Logs de autenticação e auditoria

Estruture logs com campos padronizados:

{
  "event": "login_failed",
  "user": "admin",
  "ip": "192.168.1.1",
  "timestamp": "2025-06-10T14:30:00Z",
  "reason": "invalid_password"
}

7.2. Identificação de padrões anômalos

Monitore:

  • Múltiplas tentativas de login do mesmo IP em curto período.
  • Tokens usados de diferentes geolocalizações em segundos.
  • Replay de tokens (mesmo JWT usado em endpoints diferentes rapidamente).

7.3. Resposta a incidentes

Tenha um plano:

  • Revogação em massa: invalidar todos os tokens de um usuário ou serviço.
  • Bloqueio de serviço: desabilitar endpoints comprometidos no gateway.
  • Notificação: alertar a equipe de segurança via SIEM.
# Script de revogação em massa no Redis
redis-cli KEYS "blacklist:*" | xargs redis-cli DEL

8. Testes de Segurança e Validação Contínua

8.1. Testes de penetração focados em autenticação

Teste cenários como:

  • JWT com algoritmo none (tentativa de bypass).
  • Token com expiração no passado.
  • Injeção de claims maliciosas no payload.
# Exemplo de teste: JWT com alg=none
header: {"alg": "none"}
payload: {"sub": "admin", "role": "superadmin"}
# Token: eyJhbGciOiJub25lIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJzdXBlcmFkbWluIn0.

8.2. Validação de claims e expiração em pipelines de CI/CD

Adicione testes automatizados:

# Teste unitário em Python
def test_jwt_expired():
    token = create_expired_token()
    with pytest.raises(Unauthorized):
        validate_token(token)

8.3. Checklists de segurança para deploy de novos serviços

Checklist mínimo:

  • [ ] O serviço valida JWTs com chave pública?
  • [ ] Tokens de serviço têm escopo restrito?
  • [ ] Headers internos são removidos no gateway?
  • [ ] Logs não contêm tokens?
  • [ ] Rate limiting está configurado?

Referências