Validação de Assinatura HMAC-SHA256
Verificação criptográfica de autenticidade, headers anti-replay e exemplos em Node.js e Python.
Para garantir que as notificações HTTP recebidas em seu endpoint foram de fato enviadas pela NFER e que seu conteúdo não foi interceptado nem alterado no trânsito, cada requisição inclui uma assinatura criptográfica HMAC-SHA256.
1. Cabeçalhos HTTP Enviados pela NFER
| Cabeçalho HTTP | Exemplo de Valor | Finalidade |
|---|---|---|
X-NFER-Signature | sha256=d3b07384...5f31 | Assinatura hexadecimal calculada via HMAC-SHA256 sobre ${timestamp}.${rawBody}. |
X-NFER-Timestamp | 1726859100 | Timestamp UNIX em segundos do momento do despacho para prevenção de ataques de replay. |
X-NFER-Event | nfe.authorized | Nome canônico do evento contido no payload. |
X-NFER-Delivery | dlv_01HXYZ7890ABCDEF | Identificador único desta tentativa de entrega para logs e rastreabilidade. |
User-Agent | NFER-Webhooks/1.0 | Identificação oficial do cliente HTTP da NFER. |
2. Como a Assinatura é Calculada
O cálculo da assinatura vincula o timestamp ao corpo bruto (raw body) da requisição:
signed_payload = timestamp + "." + raw_body
expected_signature = "sha256=" + HMAC_SHA256(signed_payload, webhook_secret)
expected_signature = "sha256=" + HMAC_SHA256(signed_payload, webhook_secret)
Utilize Sempre o Raw Body
Ao validar a assinatura, nunca utilize o JSON já parseado ou re-serializado (ex:
JSON.stringify(req.body)). Diferenças de ordenação de chaves ou espaços em branco invalidam o hash. Configure seu framework para preservar o buffer bruto (express.raw() ou similar).3. Exemplos de Implementação
Node.js / Express (TypeScript)
import express, { Request, Response } from 'express';
import crypto from 'crypto';
const app = express();
const WEBHOOK_SECRET = process.env.NFER_WEBHOOK_SECRET || 'whsec_...';
// IMPORTANTE: Capturar o corpo da requisição em formato Buffer bruto
app.post(
'/webhooks/nfer',
express.raw({ type: 'application/json' }),
(req: Request, res: Response) => {
const signature = req.headers['x-nfer-signature'] as string;
Python / FastAPI
from fastapi import FastAPI, Request, HTTPException, status
import hmac
import hashlib
import time
import os
app = FastAPI()
WEBHOOK_SECRET = os.getenv("NFER_WEBHOOK_SECRET", "whsec_...")
@app.post("/webhooks/nfer")
async def nfer_webhook(request: Request):
signature = request.headers.get("x-nfer-signature")