Segurança em APIs REST: boas práticas

APIs REST são a espinha dorsal da comunicação entre sistemas modernos, mas cada endpoint exposto representa uma superfície de ataque potencial. Para desenvolvedores, construir APIs seguras não é opcional — é requisito fundamental. Este artigo aborda as práticas essenciais para proteger suas APIs REST contra as ameaças mais comuns.

1. Autenticação e Autorização Robusta

O primeiro passo para proteger uma API é garantir que apenas usuários legítimos acessem os recursos. JSON Web Tokens (JWT) são amplamente utilizados, mas exigem cuidado na implementação.

// Exemplo de geração de JWT com RS256
const jwt = require('jsonwebtoken');
const privateKey = fs.readFileSync('private.pem');

const token = jwt.sign(
  { userId: 123, role: 'admin', scope: 'read:users write:users' },
  privateKey,
  { algorithm: 'RS256', expiresIn: '15m' }
);

Boas práticas:
- Use algoritmos assimétricos (RS256, ES256) em vez de HS256
- Defina expiração curta (15-30 minutos) para tokens de acesso
- Implemente refresh tokens com rotação (cada uso gera um novo)
- Mantenha uma lista de revogação para tokens comprometidos

O controle de acesso baseado em papéis (RBAC) e escopos OAuth2 permite granularidade fina:

// Middleware de autorização com RBAC
function authorize(requiredRole, requiredScope) {
  return (req, res, next) => {
    const { role, scope } = req.user;
    if (role !== requiredRole || !scope.includes(requiredScope)) {
      return res.status(403).json({ error: 'Acesso negado' });
    }
    next();
  };
}

2. Validação e Sanitização de Entradas

Nunca confie em dados vindos do cliente. Toda entrada deve ser validada e sanitizada antes do processamento.

// Validação com whitelist de parâmetros
const allowedParams = ['name', 'email', 'age'];
const unsafeParams = Object.keys(req.body);

unsafeParams.forEach(param => {
  if (!allowedParams.includes(param)) {
    delete req.body[param];
  }
});

// Sanitização contra injeção
const sanitizedEmail = req.body.email.replace(/[<>"'&]/g, '');

Regras essenciais:
- Valide tipos, formatos e tamanhos máximos
- Use listas brancas (whitelist) para parâmetros permitidos
- Sanitize entradas contra SQL/NoSQL injection e command injection
- Utilize bibliotecas especializadas como validator.js ou joi

3. Proteção contra Ataques Comuns

APIs REST são alvos frequentes de XSS, CSRF e outros ataques. A configuração correta de headers HTTP é sua primeira linha de defesa.

// Configuração de headers de segurança
app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy', "default-src 'self'");
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  next();
});

// CORS restritivo
const corsOptions = {
  origin: ['https://meudominio.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true
};
app.use(cors(corsOptions));

Para CSRF, implemente tokens anti-CSRF e utilize cookies com atributo SameSite:

// Cookie seguro com SameSite
res.cookie('session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 3600000
});

4. Limitação de Taxa e Controle de Tráfego

Rate limiting protege sua API contra abusos, ataques de força bruta e scraping.

// Implementação com express-rate-limit
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutos
  max: 100, // limite por IP
  message: { error: 'Muitas requisições. Tente novamente mais tarde.' },
  standardHeaders: true,
  legacyHeaders: false
});

app.use('/api/', limiter);

// Limite mais restritivo para login
const loginLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // 1 hora
  max: 5,
  skipSuccessfulRequests: true
});
app.post('/api/login', loginLimiter, loginHandler);

Estratégias adicionais:
- Implemente throttling progressivo (aumenta o tempo de espera gradualmente)
- Bloqueie temporariamente IPs após múltiplas falhas de autenticação
- Monitore padrões anômalos como bursts de requisições ou tentativas de brute force

5. Transporte Seguro e Headers de Segurança

Toda comunicação com sua API deve ocorrer exclusivamente via HTTPS com TLS 1.2 ou superior.

// Configuração de servidor HTTPS
const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.cert'),
  secureOptions: require('constants').SSL_OP_NO_TLSv1 | require('constants').SSL_OP_NO_TLSv1_1,
  ciphers: 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'
};

https.createServer(options, app).listen(443);

// HSTS header
app.use((req, res, next) => {
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  next();
});

Headers obrigatórios:
- X-Content-Type-Options: nosniff
- Cache-Control: no-store para dados sensíveis
- Desative versões inseguras do protocolo (SSLv3, TLS 1.0/1.1)

6. Tratamento de Erros e Logging Seguro

Erros vazam informações valiosas para atacantes. Seja genérico nas respostas e cuidadoso nos logs.

// Tratamento global de erros
app.use((err, req, res, next) => {
  const errorId = uuidv4();

  // Log seguro (sem dados sensíveis)
  logger.error({
    errorId,
    message: err.message,
    stack: err.stack,
    method: req.method,
    path: req.path,
    userId: req.user?.id
  });

  // Resposta genérica
  res.status(500).json({
    error: 'Erro interno do servidor',
    errorId
  });
});

Regras de logging:
- Nunca logue senhas, tokens, dados de cartão de crédito ou PII
- Use IDs de correlação para rastreamento sem expor informações críticas
- Implemente níveis de log (info, warn, error) para facilitar auditoria

7. Práticas de Versionamento e Gerenciamento de Dependências

APIs evoluem, e versões antigas podem conter vulnerabilidades conhecidas.

// Versionamento explícito
app.use('/api/v1/users', usersRouterV1);
app.use('/api/v2/users', usersRouterV2);

// Deprecação segura
app.use('/api/v1/users', (req, res, next) => {
  res.setHeader('Warning', '299 - "API version 1 is deprecated. Use v2."');
  next();
});

Auditoria de dependências:

// npm audit para verificar vulnerabilidades
npm audit --production

// Atualização automática com Dependabot ou Renovate
// Verificação de CVEs em bibliotecas

Mantenha um inventário de todas as dependências e estabeleça um processo para atualização imediata de correções de segurança críticas.

Conclusão

Segurança em APIs REST não é um destino, mas um processo contínuo. As práticas descritas neste artigo formam uma base sólida, mas devem ser complementadas com testes de penetração regulares, análise de vulnerabilidades e atualização constante. Lembre-se: a segurança da sua API depende do elo mais fraco da corrente — e cada desenvolvedor é responsável por fortalecer esse elo.

Referências