Segurança em APIs REST: além do básico de autenticação

1. Controle de Acesso Granular: Autorização por Escopo e Recursos

1.1. OAuth 2.0 e OpenID Connect: escopos, claims e validação de tokens JWT

Autenticação básica (usuário/senha) é insuficiente para APIs modernas. O OAuth 2.0 com OpenID Connect permite controle granular via escopos e claims JWT.

Exemplo de token JWT com escopos:

{
  "iss": "https://auth.example.com",
  "sub": "user_12345",
  "aud": "api.example.com",
  "exp": 1719000000,
  "iat": 1718913600,
  "scope": "read:orders write:orders admin:users",
  "claims": {
    "role": "manager",
    "department": "finance",
    "region": "us-east"
  }
}

Validação de token em middleware:

function validateToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1]
  if (!token) return res.status(401).json({ error: 'Token ausente' })

  try {
    const decoded = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] })

    // Verificar escopo específico
    if (!decoded.scope.includes('read:orders')) {
      return res.status(403).json({ error: 'Escopo insuficiente' })
    }

    req.user = decoded
    next()
  } catch (err) {
    return res.status(401).json({ error: 'Token inválido' })
  }
}

1.2. RBAC vs. ABAC em APIs REST

RBAC (Role-Based Access Control) atribui permissões por cargo, enquanto ABAC (Attribute-Based Access Control) avalia atributos do usuário, recurso e ambiente.

Exemplo de política ABAC:

POLICY: Acesso a relatórios financeiros
  IF user.department == "finance" 
     AND user.region == resource.region 
     AND time.between("09:00", "18:00")
  THEN ALLOW read
  ELSE DENY

Implementação de RBAC simples:

const roles = {
  admin: ['read:any', 'write:any', 'delete:any'],
  manager: ['read:orders', 'write:orders'],
  viewer: ['read:orders']
}

function authorize(role, action) {
  return roles[role]?.includes(action) || false
}

1.3. Gateways de API e PDP (Policy Decision Point)

Gateways centralizam políticas de segurança, delegando decisões a um PDP externo.

Arquitetura com PDP:

Cliente → Gateway API → PDP (Policy Decision Point) → Serviço
                ↓
          Policy Enforcement Point (PEP)

Exemplo de requisição ao PDP:

POST /pdp/authorize
{
  "subject": { "id": "user_123", "role": "manager" },
  "resource": { "type": "order", "id": "ORD-456" },
  "action": "read",
  "context": { "ip": "192.168.1.10", "time": "2025-06-20T14:30:00Z" }
}

RESPONSE: { "decision": "PERMIT", "obligations": ["log_access"] }

2. Proteção contra Injeção e Manipulação de Dados

2.1. Validação de entrada e sanitização

Injeções ocorrem quando dados não confiáveis são interpretados como comandos.

Exemplo de SQL Injection prevenida:

// VULNERÁVEL
query = "SELECT * FROM users WHERE id = " + req.params.id

// SEGURO (parametrização)
query = "SELECT * FROM users WHERE id = $1"
result = db.execute(query, [req.params.id])

Sanitização para NoSQL:

function sanitizeMongoQuery(input) {
  // Remove operadores $ perigosos
  const dangerous = ['$where', '$ne', '$gt', '$regex']
  return JSON.parse(JSON.stringify(input), (key, value) => {
    if (dangerous.includes(key)) return undefined
    return value
  })
}

2.2. Mass Assignment Protection

Evite que usuários modifiquem campos não autorizados via requisições bulk.

DTOs específicos por operação:

// DTO para criação de usuário
{
  "allowed_fields": ["name", "email", "password"]
}

// DTO para atualização de perfil
{
  "allowed_fields": ["name", "phone", "avatar_url"]
  "blocked_fields": ["role", "is_admin", "balance"]
}

Implementação de whitelisting:

function filterFields(body, allowedFields) {
  const filtered = {}
  for (const field of allowedFields) {
    if (body[field] !== undefined) {
      filtered[field] = body[field]
    }
  }
  return filtered
}

// Uso no endpoint
app.post('/users', (req, res) => {
  const safeBody = filterFields(req.body, ['name', 'email', 'password'])
  // Processa apenas campos seguros
})

2.3. Rate Limiting e Throttling inteligente

Proteja contra força bruta e DDoS com limites adaptativos.

Configuração de rate limiting:

// Por endpoint sensível
POST /api/login → 5 requisições/minuto por IP
POST /api/reset-password → 3 requisições/hora por email
GET /api/public → 100 requisições/minuto por IP

// Throttling adaptativo
if (error_rate > 0.1) {
  reduce_limit_by(50%)
}
if (suspicious_pattern_detected) {
  block_for(15, 'minutes')
}

3. Segurança na Camada de Transporte e Criptografia

3.1. TLS 1.3 obrigatório

Configuração mínima para APIs modernas:

# Nginx - cipher suites seguras
ssl_protocols TLSv1.3;
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
ssl_prefer_server_ciphers on;

# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";

3.2. Criptografia de payload sensível

Criptografia de campo vs. registro:

// Criptografia de campo (dados específicos)
{
  "user_id": "usr_789",
  "encrypted_ssn": "aes_gcm_encrypted_base64_data",
  "name": "João Silva"  // campo não sensível
}

// Criptografia de registro (todo o payload)
{
  "encrypted_payload": "aes_gcm_encrypted_base64_data",
  "iv": "base64_iv",
  "tag": "base64_auth_tag"
}

3.3. mTLS para autenticação mútua

Ideal para comunicação entre microserviços:

// Configuração de cliente mTLS
curl --cert client.crt --key client.key \
     --cacert ca.crt \
     https://service.internal/api/data

// Verificação no servidor
if (!client_certificate.valid || 
    !client_certificate.cn in allowed_services) {
  return 403 Forbidden
}

4. Prevenção de Ataques Comuns em APIs REST

4.1. CSRF e CORS configurados corretamente

CORS restritivo:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Proteção CSRF para APIs baseadas em cookie:

// Gerar token CSRF
csrf_token = crypto.randomBytes(32).toString('hex')
set_cookie('csrf_token', csrf_token, { httpOnly: true, sameSite: 'strict' })

// Validar em mutações
if (req.headers['x-csrf-token'] !== get_cookie('csrf_token')) {
  return 403 Forbidden
}

4.2. Proteção contra IDOR e Path Traversal

IDOR prevention:

// Em vez de depender do ID enviado
GET /api/orders/ORD-123

// Verificar propriedade do recurso
function getOrder(orderId, userId) {
  const order = db.orders.findById(orderId)
  if (order.user_id !== userId) {
    throw new ForbiddenError('Acesso negado')
  }
  return order
}

Path traversal prevention:

function sanitizePath(userInput) {
  // Remove caracteres de path traversal
  const safe = userInput.replace(/\.\.\//g, '')
                         .replace(/\.\.\\/g, '')
                         .replace(/\0/g, '')
  // Garante que está dentro do diretório permitido
  if (safe.includes('/') || safe.includes('\\')) {
    throw new Error('Caminho inválido')
  }
  return safe
}

4.3. Mitigação de ataques de Replay com Nonce e Timestamp

Implementação de nonce:

// Cliente inclui nonce e timestamp
{
  "nonce": "a1b2c3d4e5f6",
  "timestamp": 1718913600,
  "data": { ... }
}

// Servidor valida
function validateNonce(nonce, timestamp) {
  // Verifica se timestamp é recente (5 minutos)
  if (Math.abs(Date.now()/1000 - timestamp) > 300) {
    return false
  }
  // Verifica se nonce já foi usado
  if (redis.exists(`nonce:${nonce}`)) {
    return false
  }
  // Marca nonce como usado
  redis.set(`nonce:${nonce}`, 'used', 'EX', 300)
  return true
}

5. Logging, Monitoramento e Resposta a Incidentes

5.1. Logging seguro

O que registrar e o que nunca registrar:

// NUNCA registrar
senhas, tokens JWT, números de cartão, SSN/CPF, chaves de API

// SEMPRE registrar
correlation_id, user_id (anonimizado), endpoint, método HTTP,
status code, timestamp, IP (anonimizado), user-agent

Exemplo de log seguro:

{
  "correlation_id": "req_abc123",
  "user_id": "usr_***789",  // anonimizado
  "method": "POST",
  "endpoint": "/api/orders",
  "status": 201,
  "ip": "192.168.1.***",  // anonimizado
  "timestamp": "2025-06-20T14:30:00Z",
  "duration_ms": 45
}

5.2. Auditoria com correlation IDs

// Gerar correlation ID no gateway
function generateCorrelationId() {
  return `req_${uuid.v4()}`
}

// Propagar para todos os serviços
X-Correlation-ID: req_abc123-def456-ghi789

5.3. Detecção de anomalias

Regras de detecção automatizada:

// Anomalias comuns
if (requests_per_minute > threshold * 10) → possível DDoS
if (error_rate > 0.2) → possível ataque ou falha
if (same_user_agent + different_ips) → scraping
if (sequential_ids + unauthorized_access) → IDOR attempt

// Resposta automatizada via webhook
if (anomaly_detected) {
  webhook.send({
    type: 'security_alert',
    action: 'block_ip',
    ip: attacker_ip,
    duration: 3600
  })
}

6. Gestão de Segredos e Ciclo de Vida de Credenciais

6.1. Armazenamento seguro com cofres de segredos

// HashiCorp Vault - exemplo de acesso
vault write secret/api/production \
  api_key="sk_live_abc123" \
  db_password="s3cur3P@ss"

// Aplicação lê do vault, não do código
api_key = vault.read('secret/api/production').data.api_key

6.2. Rotação automática de credenciais

// Política de rotação
- Tokens de acesso: expiração em 15 minutos
- Tokens de refresh: expiração em 7 dias
- Chaves de API: rotação a cada 90 dias
- Certificados mTLS: renovação a cada 30 dias

// Rotação automática
if (token.exp < now + 300) {  // 5 minutos antes de expirar
  new_token = refreshToken(refresh_token)
  update_stored_token(new_token)
}

6.3. Revogação de tokens: blocklists vs. allowlists

// Blocklist (lista negra) - para revogação imediata
redis.sadd('token:blocklist', token_jti)
redis.expire('token:blocklist', 86400)  // 24 horas

// Allowlist (lista branca) - para controle granular
redis.sadd('user:123:active_tokens', token_jti)
if (!redis.sismember('user:123:active_tokens', token_jti)) {
  return 401 Unauthorized
}

7. Segurança em Documentação e Headers HTTP

7.1. Headers de segurança essenciais

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

7.2. Exposição controlada de metadados

// Nunca expor
X-Powered-By: Express 4.18.2
Server: Apache/2.4.41 (Ubuntu)
X-Debug: true

// Configuração segura
app.disable('x-powered-by')
helmet()  // Remove headers informativos

7.3. Documentação segura de APIs

Exemplo de OpenAPI com segurança explícita:

openapi: 3.0.0
info:
  title: API de Pedidos
  version: 1.0.0
  description: "Documentação segura - requer autenticação"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "Token JWT com escopos: read:orders, write:orders"

paths:
  /orders:
    get:
      summary: Listar pedidos
      security:
        - bearerAuth: [read:orders]
      responses:
        '200':
          description: Lista de pedidos
        '403':
          description: Escopo insuficiente

Referências