API keys são credenciais usadas para identificar aplicações, integrações e consumidores de uma API. Elas são simples de operar, mas também fáceis de vazar em repositórios, logs, URLs e frontends. Uma implementação segura precisa gerar chaves fortes, armazenar apenas hashes, limitar escopo, permitir rotação e aplicar rate limiting.
Formato da chave
Use um prefixo identificável e um segredo aleatório:
import crypto from 'node:crypto';
function createApiKey() {
const id = crypto.randomBytes(8).toString('hex');
const secret = crypto.randomBytes(32).toString('base64url');
return {
id,
secret,
value: `cf_live_${id}_${secret}`,
};
}O prefixo ajuda scanners e equipes a reconhecer a credencial. Separe ambientes com prefixos como test e live.
Armazene somente hash
function hashApiKey(secret) {
return crypto.createHash('sha256').update(secret).digest('hex');
}
await apiKeys.insert({
id,
secretHash: hashApiKey(secret),
ownerId,
scopes: ['orders:read'],
createdAt: new Date(),
});Mostre a chave completa apenas uma vez. Depois, exiba somente prefixo e últimos caracteres.
Por que SHA-256 pode ser adequado
API keys bem geradas possuem alta entropia e não são escolhidas por humanos. Um hash rápido é suficiente para comparação e evita o custo de Argon2 em toda requisição. Senhas humanas continuam exigindo Argon2 ou outra função lenta.
Header de autenticação
Authorization: ApiKey cf_live_abcd_...Também é comum X-API-Key. Não envie a chave em query string, porque URLs aparecem em histórico, analytics, proxies e logs.
Middleware
async function requireApiKey(req, res, next) {
const header = req.get('authorization') || '';
const match = header.match(/^ApiKey (.+)$/);
if (!match) {
res.status(401).json({ error: 'missing_api_key' });
return;
}
const parsed = parseApiKey(match[1]);
if (!parsed) {
res.status(401).json({ error: 'invalid_api_key' });
return;
}
const record = await apiKeys.findById(parsed.id);
if (!record || record.revokedAt) {
res.status(401).json({ error: 'invalid_api_key' });
return;
}
const received = Buffer.from(hashApiKey(parsed.secret), 'hex');
const expected = Buffer.from(record.secretHash, 'hex');
if (!crypto.timingSafeEqual(received, expected)) {
res.status(401).json({ error: 'invalid_api_key' });
return;
}
req.apiKey = record;
next();
}Valide tamanhos antes de usar timingSafeEqual.
Escopos
Não crie chaves com acesso total por padrão:
function requireScope(scope) {
return (req, res, next) => {
if (!req.apiKey.scopes.includes(scope)) {
res.status(403).json({ error: 'insufficient_scope' });
return;
}
next();
};
}Defina escopos estáveis como orders:read, orders:write e webhooks:manage.
Chave por integração
Crie uma chave para cada sistema, ambiente e finalidade. Compartilhar uma chave entre equipes impede rastrear uso e revogar apenas o consumidor comprometido.
Expiração
Chaves podem ter data de expiração, especialmente para acessos temporários. Envie alertas antes do vencimento e permita sobreposição durante rotação.
Rotação
- crie uma nova chave;
- distribua por canal seguro;
- observe uso da nova;
- revogue a antiga;
- registre o evento.
Evite substituir o segredo no mesmo registro sem período de transição.
Revogação
A revogação deve ser imediata e refletida em caches. Se a validação usa cache local, aplique TTL curto ou mecanismo de invalidação.
Last used
Registre último uso de forma assíncrona e limitada, para não escrever no banco em toda requisição. Esse dado ajuda a remover chaves inativas.
Rate limiting
Limite por key ID e, quando apropriado, por IP e operação. Um atacante com chave roubada não deve consumir capacidade ilimitada.
Quotas
Rate limit controla janelas curtas; quota controla volume diário ou mensal. Não use valores financeiros ou planos vindos de headers do cliente.
Restrição por IP
Allowlist de IP pode complementar, mas não substitui a chave. Redes mudam, proxies compartilhados existem e IP pode não representar identidade confiável.
mTLS
Integrações críticas podem combinar API key com certificado de cliente. Isso reduz o risco de uma chave isolada ser suficiente.
Frontends
Uma chave embutida em JavaScript, aplicativo móvel ou extensão não é segredo. Use-a apenas para identificação pública e aplique limites. Operações privilegiadas precisam de autenticação de usuário ou backend intermediário.
Webhooks
Para webhooks, prefira assinatura HMAC do corpo com timestamp e proteção contra replay. Uma API key estática em header pode ser usada, mas não valida integridade do payload da mesma forma.
Logs
Redija Authorization e X-API-Key:
const logger = pino({
redact: [
'req.headers.authorization',
'req.headers.x-api-key',
],
});Registre key ID, nunca o segredo.
Detecção de vazamento
Use secret scanning em Git, CI e plataformas de código. Prefixos exclusivos facilitam regras. Se uma chave aparecer em repositório público, revogue imediatamente; remover o commit não é suficiente.
Armazenamento pelo cliente
Clientes server-side devem guardar em secret manager ou variável injetada. Não coloque em Dockerfile, imagem, arquivo versionado ou script de build.
Respostas de erro
Use 401 para ausente ou inválida e 403 para escopo insuficiente. Não revele se o ID existia, expirou ou foi revogado.
Auditoria
Registre criação, rotação, revogação, mudança de escopo e uso administrativo. Logs de auditoria devem ser imutáveis e separados dos logs operacionais.
Erros comuns
- guardar chave em texto puro;
- enviar em URL;
- usar chave curta;
- uma chave para todos os clientes;
- sem escopos;
- sem rate limit;
- não permitir rotação;
- colocar chave secreta no frontend;
- registrar headers;
- cachear revogação por muito tempo.
Testes
Teste chave válida, segredo incorreto, ID inexistente, revogada, expirada, escopo insuficiente, rotação, cache e rate limiting.
Fluxo recomendado
- gere alta entropia;
- use prefixo e ID;
- armazene hash;
- mostre uma única vez;
- defina escopos mínimos;
- limite uso;
- permita rotação;
- revogue rapidamente;
- redija logs;
- audite alterações.
Combine API keys com Secret Management, Rate Limiting, Pino, TLS e HTTPS e Idempotency Keys.
Consulte o guia de segurança REST da OWASP e a documentação oficial de crypto no Node.js.




