Pular para o conteúdo

Assinatura de webhook

Toda entrega leva uma assinatura HMAC-SHA256. Ela prova que a requisição veio da owiew e que o corpo não foi alterado. A verificação é obrigatória: um receptor que não verifica aceita qualquer pessoa que descubra a URL.

assinatura = hex_minusculo( HMAC-SHA256( segredo, X-Owiew-Timestamp + "." + corpo_bruto ) )
  • segredo é o segredo da inscrição, mostrado uma única vez na criação.
  • X-Owiew-Timestamp é o texto do cabeçalho, em segundos, sem espaço.
  • corpo_bruto são os bytes exatos recebidos. Não decodifique e reserialize o JSON antes de verificar: qualquer diferença de espaço ou ordem de campos muda a assinatura.

O cabeçalho X-Owiew-Signature tem o formato v1=<hex>. A parte v1 é a versão do esquema.

  1. Leia o corpo como bytes, com limite de 256 KiB.
  2. Leia X-Owiew-Timestamp. Se não for um inteiro, recuse.
  3. Compare com o seu relógio. Se a diferença passar de 5 minutos, para mais ou para menos, recuse. Isso barra a reapresentação de uma requisição antiga capturada por terceiros.
  4. Para cada segredo válido, calcule a assinatura esperada.
  5. Separe X-Owiew-Signature por vírgula, mantenha as partes que começam com v1= e compare cada uma com cada esperada em tempo constante (timingSafeEqual em Node, hmac.Equal em Go). Aceite se qualquer par conferir.
  6. Se nada conferir, responda 401 sem explicar o motivo.
  7. Só depois de verificar, interprete o JSON e aplique a idempotência por X-Owiew-Delivery-Id.

Mantenha o relógio do servidor sincronizado (NTP). Relógio fora de hora faz entregas legítimas serem recusadas e dispara repetições.

A cada tentativa de entrega, a owiew calcula um timestamp novo e assina de novo. Uma repetição minutos depois da primeira falha tem timestamp novo e passa na janela de 5 minutos. O X-Owiew-Delivery-Id continua o mesmo, e é ele que você usa para deduplicar. A janela de tempo limita a reapresentação de uma captura antiga, e a deduplicação por identificador limita a reapresentação dentro da janela. Use as duas.

Quando o segredo é rotacionado, o anterior continua valendo por 24 horas. Nesse período cada entrega leva duas assinaturas, a do segredo novo primeiro:

X-Owiew-Signature: v1=<assinatura com o segredo novo>, v1=<assinatura com o segredo antigo>

Aceite a entrega se qualquer assinatura conferir com qualquer segredo que você tenha. O roteiro seguro:

  1. Peça a rotação. O segredo novo é mostrado uma única vez.
  2. Coloque o segredo novo no cofre e deixe o antigo ao lado dele.
  3. Faça o deploy do receptor que aceita os dois.
  4. Passadas as 24 horas, remova o antigo.

Use este vetor para validar a sua implementação. Os valores são de exemplo.

Item Valor
Segredo segredo-de-exemplo
Timestamp 1800000000
Corpo {"tipo":"instalacao.teste.v1"}
Assinatura esperada v1=328f40657fa58788f8633269a9d7c02fed3dfc84ef8f127f41c9e437fbc46807

Texto assinado: 1800000000.{"tipo":"instalacao.teste.v1"}. Calcule o HMAC-SHA256 desse texto com o segredo acima: o resultado precisa ser o valor da tabela. Os testes dos exemplos em Node e Go geram a assinatura pelo mesmo cálculo.

  • Verificar sobre o JSON reserializado, e não sobre os bytes recebidos.
  • Comparar com == em vez de comparação em tempo constante.
  • Esquecer o ponto entre o timestamp e o corpo.
  • Aceitar apenas a primeira assinatura durante a rotação.
  • Usar um framework que já consumiu o corpo (middleware de JSON) antes da verificação. Capture o corpo bruto.
  • Responder 200 antes de verificar.