Verificacao de Assinatura
Toda notificacao de webhook enviada pela TapSign inclui uma assinatura digital no header X-Webhook-Signature. Essa assinatura garante que a requisicao foi realmente enviada pela TapSign e que o conteudo nao foi adulterado.
Como funciona
- A TapSign gera um HMAC-SHA256 do corpo da requisicao usando o
secretdo webhook - O hash e enviado no header
X-Webhook-Signature - Sua aplicacao recalcula o hash e compara com o recebido
Nunca processe um webhook sem verificar a assinatura. Sem essa validacao, qualquer pessoa poderia enviar requisicoes falsas para sua URL.
Header de assinatura
X-Webhook-Signature: sha256=a1b2c3d4e5f6...
O valor segue o formato sha256={hash}, onde {hash} e o HMAC-SHA256 do corpo da requisicao em hexadecimal.
Exemplos de codigo
Node.js
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Uso com Express
app.post('/webhooks/tapsign', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Assinatura invalida');
}
const event = JSON.parse(payload);
console.log('Evento recebido:', event.event);
// Processar evento...
res.status(200).send('OK');
});
Java / Kotlin
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
fun verifyWebhookSignature(payload: String, signature: String, secret: String): Boolean {
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(secret.toByteArray(), "HmacSHA256"))
val expectedHash = mac.doFinal(payload.toByteArray())
.joinToString("") { "%02x".format(it) }
val expectedSignature = "sha256=$expectedHash"
return signature == expectedSignature
}
// Uso com Spring Boot
@RestController
class WebhookController {
@Value("\${tapsign.webhook.secret}")
lateinit var webhookSecret: String
@PostMapping("/webhooks/tapsign")
fun handleWebhook(
@RequestBody payload: String,
@RequestHeader("X-Webhook-Signature") signature: String
): ResponseEntity<String> {
if (!verifyWebhookSignature(payload, signature, webhookSecret)) {
return ResponseEntity.status(401).body("Assinatura invalida")
}
val event = objectMapper.readTree(payload)
println("Evento recebido: ${event["event"].asText()}")
// Processar evento...
return ResponseEntity.ok("OK")
}
}
Python
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected_hash = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
expected_signature = f"sha256={expected_hash}"
return hmac.compare_digest(signature, expected_signature)
# Uso com Flask
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/webhooks/tapsign', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Webhook-Signature')
payload = request.get_data()
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
abort(401, 'Assinatura invalida')
event = request.get_json()
print(f"Evento recebido: {event['event']}")
# Processar evento...
return 'OK', 200
Sempre utilize funcoes de comparacao em tempo constante (timingSafeEqual, compare_digest) para evitar ataques de timing.
Politica de retentativas
Quando a entrega de um webhook falha (qualquer resposta diferente de HTTP 2xx), a TapSign reenvia automaticamente seguindo este cronograma:
| Tentativa | Intervalo |
|---|---|
| 1a | Imediata |
| 2a | 1 minuto |
| 3a | 5 minutos |
| 4a | 30 minutos |
| 5a | 2 horas |
Apos 5 tentativas sem sucesso, o evento e movido para uma dead-letter queue. Voce pode reenviar manualmente pelo endpoint de reenvio de entrega ou pelo painel da TapSign.
Boas praticas
1. Responda rapidamente
Sua aplicacao deve retornar HTTP 200 o mais rapido possivel (idealmente em menos de 5 segundos). Processe o evento de forma assincrona se necessario.
// Bom: responde imediatamente e processa depois
app.post('/webhooks/tapsign', (req, res) => {
res.status(200).send('OK');
processEventAsync(req.body); // fila, worker, etc.
});
2. Seja idempotente
O mesmo evento pode ser entregue mais de uma vez (em caso de retentativa). Use o campo id do evento para evitar processamento duplicado.
async function processEvent(event) {
const alreadyProcessed = await db.webhookEvents.findById(event.id);
if (alreadyProcessed) return; // ja processado, ignorar
await db.webhookEvents.create({ id: event.id, processedAt: new Date() });
// processar evento...
}
3. Sempre valide a assinatura
Nunca confie cegamente em uma requisicao recebida. Sempre verifique o header X-Webhook-Signature antes de processar o evento.
4. Use HTTPS
Sua URL de webhook deve obrigatoriamente usar HTTPS para proteger os dados em transito.
5. Registre tudo em log
Mantenha logs de todos os webhooks recebidos para facilitar a depuracao de problemas. Registre pelo menos: event.id, event.event, timestamp e o resultado do processamento.