Modeler Webhook & Idempotensi Kriptografi
Amankan komunikasi antar sistem dari duplikasi transaksi dan manipulasi payload. Terapkan verifikasi signature HMAC-SHA256, deteksi replay attack, dan penguncian idempotensi atomik.

Contoh: invoice.paid, user.created
Kunci rahasia bersama (Shared Secret)
Sintaks native zero-dependency
Implementasi Verifikasi Signature & Idempotency Key (TYPESCRIPT)
import crypto from 'crypto';
// 1. Verifikasi Signature HMAC-SHA256 & Timestamp Replay Attack
export function verifyWebhookSignature(payload: string, signatureHeader: string, secret: string) {
// Signature format: t=1729340000,v1=abcdef0123456789...
const parts = Object.fromEntries(signatureHeader.split(',').map(kv => kv.split('=')));
const timestamp = parts['t'];
const expectedSignature = parts['v1'];
// Cegah Replay Attack: Toleransi waktu maksimal 5 menit (300 detik)
const currentTimestamp = Math.floor(Date.now() / 1000);
if (Math.abs(currentTimestamp - parseInt(timestamp, 10)) > 300) {
throw new Error('Replay attack detected: timestamp out of tolerance window');
}
// Hitung HMAC SHA-256 dari timestamp.payload
const signedPayload = `${timestamp}.${payload}`;
const computedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Gunakan timingSafeEqual untuk mencegah timing attack
const isMatch = crypto.timingSafeEqual(
Buffer.from(computedSignature, 'utf8'),
Buffer.from(expectedSignature, 'utf8')
);
if (!isMatch) {
throw new Error('Invalid cryptographic webhook signature');
}
return true;
}
// 2. Pola Idempotensi Kunci Transaksi dengan Redis Atomic Lock
export async function processIdempotentWebhook(redisClient: any, idempotencyKey: string, handler: () => Promise<void>) {
// SET key value NX EX 86400 (Atomic lock 24 jam)
const acquired = await redisClient.set(`webhook:idempotency:${idempotencyKey}`, 'PROCESSING', 'NX', 'EX', 86400);
if (!acquired) {
// Event ini sudah pernah diproses atau sedang diproses: kembalikan 200 OK seketika
return { status: 'DUPLICATE_IGNORED' };
}
try {
await handler();
await redisClient.set(`webhook:idempotency:${idempotencyKey}`, 'COMPLETED', 'XX', 'EX', 86400);
return { status: 'SUCCESS' };
} catch (err) {
// Hapus lock jika gagal agar dapat dicoba ulang
await redisClient.del(`webhook:idempotency:${idempotencyKey}`);
throw err;
}
}Menyematkan stempel waktu UNIX (t=...) pada payload yang di-sign mencegah peretas menyalin paket data HTTP yang sah dan mengirimkannya kembali untuk menduplikasi transaksi.
Perbandingan string signature wajib menggunakan fungsi pembanding waktu konstan seperti crypto.timingSafeEqual atau hash_equals untuk menggagalkan analisis celah latensi CPU.
Penggunaan Redis SET NX 86400 menjamin bahwa pengiriman webhook berulang (retries) dari gateway pembayaran tidak akan pernah memproses pesanan atau memotong saldo dua kali.