Programação Por Equipe DevBras

Capa do artigo: Trocar segredo sem derrubar o sistema: aceite os dois por um tempo

Trocar segredo sem derrubar o sistema: aceite os dois por um tempo

Faça rotação de chaves e senhas sem queda: publique a nova, aceite as duas, troque quem assina, meça o uso da antiga e só então retire. E saiba quando pular tudo isso.

Era a rotação trimestral da chave que assina as chamadas entre serviços. A pessoa trocou a variável de ambiente na API, reiniciou, e a chave antiga deixou de valer no mesmo segundo. Três workers que rodavam com a configuração antiga passaram a receber 401 e a fila encheu até alguém perceber, duas horas depois. A intenção estava certa. A ordem, não. A resposta curta: rotação sem queda tem fases. Primeiro, todo mundo que confere passa a aceitar a chave antiga e a nova. Depois, quem assina troca para a nova. Você mede até a antiga parar de aparecer, e só então a retira.

A exceção é vazamento. Se a chave foi exposta, ela morre agora, e você aceita a queda como parte do corte. Rotação de rotina e resposta a incidente são rituais diferentes, e misturar os dois é a origem da maioria dos sustos.

Por que trocar de uma vez derruba

Um segredo compartilhado tem pelo menos dois lados: quem o usa para assinar ou se autenticar e quem o usa para conferir. Em produção, cada lado costuma ter várias instâncias, e elas não reiniciam todas no mesmo instante. Há workers de longa duração, jobs agendados, contêineres de versões diferentes durante um deploy gradual, um serviço de outra equipe que só atualiza na semana seguinte.

Se o conferidor passa a aceitar só a chave nova, todo emissor que ainda usa a antiga falha. Se o emissor passa a usar a nova antes de os conferidores conhecerem, todo pedido novo falha. Não existe instante em que dá para trocar dos dois lados ao mesmo tempo. Por isso a troca precisa de um período em que as duas chaves valem.

Há ainda o que já foi emitido. Um token assinado com a chave antiga continua circulando até vencer. Se a chave antiga sai antes disso, esses tokens falham, e as pessoas são deslogadas. O período de convivência precisa durar pelo menos o prazo de validade do que foi assinado.

Como fazer a rotação em fases

  1. Gere a chave nova e dê a ela um identificador, como a data. Guarde no cofre de segredos ao lado da antiga.
  2. Publique a nova como aceita. Todos os conferidores passam a aceitar as duas. Quem assina continua com a antiga. Faça o deploy e confirme que todas as instâncias estão na versão nova.
  3. Troque quem assina. Os emissores passam a usar a chave nova. Como os conferidores já a conhecem, nada falha.
  4. Espere e meça. Aguarde pelo menos o prazo máximo de validade do que foi assinado com a antiga e acompanhe quantas conferências ainda usam a antiga. O número precisa chegar a zero.
  5. Retire a antiga. Remova dos conferidores e, depois, do cofre.
  6. Registre a data e agende a próxima rotação.

O identificador da chave é o que torna a medição possível. Quem assina coloca o id junto da assinatura, e quem confere escolhe a chave pelo id e registra qual foi usada. Exemplo em Node.js com HMAC:

const crypto = require('crypto');

// CHAVES_ACEITAS="2026-10:base64novo,2026-04:base64antigo"
// CHAVE_ASSINATURA_ID="2026-10"
function carregarChaves(texto) {
  const mapa = new Map();
  for (const item of texto.split(',')) {
    const [id, valor] = item.split(':');
    mapa.set(id, Buffer.from(valor, 'base64'));
  }
  return mapa;
}

const ACEITAS = carregarChaves(process.env.CHAVES_ACEITAS);
const ID_ASSINATURA = process.env.CHAVE_ASSINATURA_ID;

function hmac(chave, corpo) {
  return crypto.createHmac('sha256', chave).update(corpo).digest('hex');
}

function assinar(corpo) {
  return ID_ASSINATURA + '.' + hmac(ACEITAS.get(ID_ASSINATURA), corpo);
}

function conferir(corpo, assinatura) {
  const [id, mac] = String(assinatura).split('.');
  const chave = ACEITAS.get(id);
  if (!chave || !mac) return false;
  const esperado = Buffer.from(hmac(chave, corpo));
  const recebido = Buffer.from(mac);
  const ok = esperado.length === recebido.length && crypto.timingSafeEqual(esperado, recebido);
  if (ok) console.log(JSON.stringify({ evento: 'assinatura_aceita', chave: id }));
  return ok;
}

Na fase 2, CHAVES_ACEITAS tem as duas e CHAVE_ASSINATURA_ID aponta para a antiga. Na fase 3, só muda o id de assinatura. Na fase 5, a antiga sai da lista. Cada fase é uma mudança de configuração pequena, que dá para desfazer. A contagem por chave no log mostra, com número, quando a antiga parou de ser usada.

O mesmo padrão em outros segredos

JWT com chave assimétrica: publique a chave pública nova no JWKS antes de assinar com ela, e lembre que os consumidores guardam o JWKS em cache. Espere o cache expirar antes de trocar quem assina. A chave pública antiga fica publicada até vencerem os tokens emitidos com ela, como em JWT que expira.

Senha de banco: o MySQL, a partir da 8.0.14, aceita senha dupla com ALTER USER 'app'@'%' IDENTIFIED BY 'nova' RETAIN CURRENT PASSWORD, e depois ALTER USER 'app'@'%' DISCARD OLD PASSWORD. Em bancos sem esse recurso, use dois usuários com as mesmas permissões e alterne entre eles a cada rotação.

Chave de API de terceiros: muitos provedores permitem duas chaves ativas ao mesmo tempo. Crie a segunda, troque a configuração, confira no painel que a primeira parou de receber chamadas e só então revogue.

Em todos os casos, o segredo novo entra pela configuração, não pela imagem do contêiner nem pelo código. Se trocar a chave exige rebuild, a rotação vai ser adiada sempre. O assunto está em segredo fora da imagem.

Quando pular as fases

Se a chave vazou, a conta muda. Cada minuto de convivência é um minuto em que quem tem a chave vazada também é aceito. Revogue a antiga imediatamente, publique a nova o mais rápido possível e aceite que alguns serviços vão falhar até receberem a configuração. A queda é o preço, e é menor que o estrago. A ordem certa nesse caso, inclusive quando o vazamento foi num commit, está em segredo no Git.

Erros comuns

  • Trocar a chave dos dois lados de uma vez. Instâncias que ainda não reiniciaram falham, e a falha aparece como erro de autenticação difícil de diagnosticar.
  • Assinar com a nova antes de todos a aceitarem. Os conferidores atrasados recusam pedidos legítimos.
  • Retirar a antiga por calendário, sem medir. Um job semanal que ninguém lembrava quebra na próxima execução.
  • Deixar a antiga aceita para sempre. A rotação vira só acréscimo, e qualquer chave antiga vazada continua valendo.
  • Chave sem identificador. Sem id, o conferidor precisa tentar todas e você não consegue medir o uso de cada uma.
  • Usar o ritual lento num vazamento. A chave exposta continua aceita durante dias de convivência planejada.

Checklist

  • Cada chave tem um identificador que viaja junto com a assinatura ou o token.
  • Os conferidores aceitam uma lista de chaves, configurável sem rebuild.
  • Há métrica ou log de qual chave foi usada em cada conferência.
  • O período de convivência é maior que a validade do que foi assinado.
  • A chave antiga só sai depois de zero uso medido.
  • Existe um procedimento separado, mais curto, para vazamento.
  • A data da última rotação está registrada.

Pense no segredo mais importante do seu sistema, aquele que assina sessão ou autentica no banco. Se você trocar o valor agora, os processos que ainda não reiniciaram continuam funcionando até serem atualizados, ou caem no mesmo segundo?

Avalie este artigo

Referência: https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html