Quando um webhook tem um segredo, toda requisição é assinada com HMAC-SHA256 sobre o cabeçalho de timestamp e o corpo bruto.
X-Mateflow-Signature = "sha256=" + hex(
HMAC-SHA256(secret, X-Mateflow-Timestamp + "." + raw_request_body)
)
- Use os bytes brutos. Caracteres especiais em HTML podem ser escapados no corpo, então interpretar o JSON e serializá-lo de novo produz bytes diferentes e a assinatura não vai coincidir.
- Pegue o timestamp do cabeçalho, não do corpo. Um reenvio manual atualiza apenas o timestamp do cabeçalho e a assinatura.
- Uma verificação de timestamp não interfere nas retentativas. Cada retentativa automática e cada reenvio manual são assinados de novo, então uma janela de 5 minutos é suficiente.
const crypto = require('crypto');
// rawBody must be the untouched bytes, e.g. from express.raw({ type: 'application/json' }).
function verifyMateflowSignature(rawBody, headers, secret, toleranceSeconds = 300) {
const timestamp = headers['x-mateflow-timestamp'];
const received = headers['x-mateflow-signature'] || '';
if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) {
return false;
}
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest('hex');
const a = Buffer.from(received);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}import hashlib
import hmac
import time
def verify_mateflow_signature(raw_body: bytes, headers, secret: str, tolerance_seconds: int = 300) -> bool:
timestamp = headers.get("X-Mateflow-Timestamp", "")
received = headers.get("X-Mateflow-Signature", "")
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > tolerance_seconds:
return False
signed = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(received.encode(), expected.encode())Vetor de teste
Use estes valores para conferir o seu cálculo da assinatura. O timestamp já expirou, então relaxe a sua janela de tempo durante os testes.
secret my_signing_secret
X-Mateflow-Timestamp 1793664000
raw body (UTF-8) {"event":"member.joined","data":{"username":"张三"}}
X-Mateflow-Signature sha256=3f4c83828ce0086150c9bd48c1654899357b7c05265c1d1b6c172225ee04b4b4Rotação de segredo
Um novo segredo entra em vigor assim que é salvo, e as retentativas na fila também são assinadas com ele. Não há período em que a Mateflow assine com os dois, então faça o seu receptor aceitar o segredo antigo e o novo durante a troca.