Verificação de Autenticidade

Obtenção da chave Secret

Para a certificação de que a mensagem é autêntica, disparada pela Creditas e direcionada ao destino esperado, utilizaremos o formato HTTP Message Signatures.
Esse mecanismo fornece integridade e autenticidade ponta a ponta para os componentes de uma mensagem HTTP.

Após realizar o devido registro nos eventos de sua escolha, cada parceiro terá acesso a um magic-link. Este se trata de uma URL de POST para requisição de uma chave secret que será utilizado durante a implementação do HMAC e verificação de autenticidade da informação recebida.

Exemplo:

curl --location --request POST 'https://webhook.stg.creditas.io/webhook/secret/058a18f8-071a-4018-b627-f6791d1b48f0/unwrap' \
--header 'Accept: application/vnd.creditas.v1+json'
{
    "secret": "d0a0a44ffb394a4841bf88df5ecc4625"
}
{
    "code": "CONSUMER_SECRET_ALREADY_CHECKED",
    "message": "Secret already checked.",
    "timestamp": "2023-05-11T12:08:50.756622"
}

⚠️

Atenção!

Guarde bem essa chave, pois para sua segurança essa url poderá ser acessada apenas uma vez. Caso perca ou necessite solicitar novamente sua secret, será preciso informar ao seu consultor para abrir um chamado para geração de um novo magic-link

O que é o HTTP Message Signatures?

"Esse mecanismo fornece integridade e autenticidade de ponta a ponta para os componentes de uma mensagem HTTP.
O mecanismo permite que as aplicações criem assinaturas digitais ou códigos de autenticação de mensagem (MACs) apenas sobre os componentes da mensagem que são significativos e apropriados para a aplicação. Regras rígidas de normalização garantem que o verificador garanta a assinatura mesmo que a mensagem tenha sido transformada em qualquer uma das muitas formas permitidas pelo HTTP.”
HTTP Message Signatures

Como verificar a autenticidade da mensagem?

Para verificar a autenticidade de uma mensagem HTTP utilizando o HTTP Message Signature, você pode seguir os seguintes passos:

const crypto = require('crypto');

// 1. Headers recebidos:
// O header "Signature" da mensagem HTTP recebida contém a assinatura da mensagem. 
// O header "Signature-input" é um campo dicionário que contém metadados da mensagem assinada, como o algoritmo de assinatura usado, a chave pública do emissor entre outros.

const headers = {
  digest: 'SHA-256=8df2bffaf24313e75ace59688f2592993d2f990e5f5c9caea851c656492f9c83',
  host: '3a2924a6a9dfb9aa173f5512cf2d54aa.m.pipedream.net',
  signature: 'webhook-param=:6974557af18a1925179c17c30e4239e8b9d68e883b2d3e13fb5498f85df3d858:',
  signature-input: 'webhook-param=("digest" "@target-uri");created=1677784215510;nonce="a8ca9147-f71d-4b02-b229-5f1a5dbb753b";alg="hmac-sha256"'
};

// 2. 'Parse' dos valores do header signature-input.
const sigInput = headers['signature-input'].split(';').reduce((prev, curr) => {
  const [key, value] = curr.split('=');
  prev[key] = value.replace(/"/g, '');
  return prev;
}, {});

// 3. Separar a Signature em seu valor e parâmetros
const [sigVal, sigParams] = headers.signature.split(':');

// 4. Verificar se a Signature usa o algoritmo esperado
if (sigInput.alg !== 'hmac-sha256') {
  throw new Error('Algoritmo de assinatura inválido');
}

// 5. Obter o secret para verificar a assinatura (informações de como conseguir essa chave estão na sessão anterior Obtenção da chave Secret)
const secret = 'your-secret-key';

// 6. Obter o valor da mensagem
const message = `digest: ${headers.digest}\nhost: ${headers.host}\n`;

// 7. Gerar a signature esperada usando HMAC-SHA256
const expectedSignature = crypto.createHmac('sha256', secret)
  .update(message + sigInput.created + sigInput.nonce + sigParams)
  .digest('hex');

// 8. Comparar as signatures para verificar a autenticidade da mensagem
if (expectedSignature !== sigVal) {
  throw new Error('Assinatura inválida');
}

console.log('Mensagem autenticada com sucesso!');

Caso as assinaturas não coincidam, a mensagem pode ter sido modificada no caminho ou pode ter sido enviada por alguém que não tem a chave privada correspondente à chave pública do emissor, ou seja, não foi enviada pela Creditas. Nesse caso, a mensagem deve ser considerada não autêntica e não deve ser processada.


Exemplo:

f4991f87cc0d202723c6fa770dbeaa28
POST https://3a2924a6a9dfb9aa173f5512cf2d54aa.m.pipedream.net
digest: SHA-256=86bf095f0999a9dbbefea0e521ba982ee4010508671799d54cf5f2d640580eff
signature: webhook-param=:f17a5e42dfea08e6e3aa15b5a3aa514592350b939955a8f8c1fff6809083a12f:
signature-input: webhook-param=("digest" "@target-uri");created=1677784172482;nonce="f1867c6e-dd2f-44c5-b7af-d0ac2ee5ec00";alg="hmac-sha256"
body: {request content}

O atributo signature-input informa quais elementos da mensagem HTTP foram assinados e em qual ordem eles devem ser incluídos no momento de gerar o input para validar a assinatura. O prefixo webhook-param é apenas para indicar que estes são os parâmetros da assinatura de mesmo prefixo. Isso porque o padrão permite que várias assinaturas sejam adicionadas à mensagem conforme ela for trafegando entre camadas/aplicações diferentes.

Neste caso, temos 2 elementos que são o header digest e a URI da mensagem:

O primeiro passo é gerar o input dos parâmetros que foram assinados, incluindo o signature-input ao final. Neste caso:

"digest": SHA-256=86bf095f0999a9dbbefea0e521ba982ee4010508671799d54cf5f2d640580eff
"@target-uri": https://3a2924a6a9dfb9aa173f5512cf2d54aa.m.pipedream.net
"@signature-param": ("digest" "@target-uri");created=1677784172482;nonce="f1867c6e-dd2f-44c5-b7af-d0ac2ee5ec00";alg="hmac-sha256"

A assinatura final que está no header signature é o HMAC-SHA256 representado em hexadecimal do input gerado no passo anterior com o secret (neste caso f4991f87cc0d202723c6fa770dbeaa28).

Este processo, deve gerar o resultado f17a5e42dfea08e6e3aa15b5a3aa514592350b939955a8f8c1fff6809083a12f conforme o recebido no header signature da requisição. O valor da assinatura é enviado com o caractere “:” no início e no final conforme especifica o padrão.

Você pode fazer esse teste usando uma ferramenta online de geração de HMAC como a https://www.devglan.com/online-tools/hmac-sha256-online, por exemplo.