← Back to all posts
2026-09-07 · MailAnvil Team

Amankan Webhook Email Transaksional — Verifikasi Signature HMAC & Cegah Serangan SSRF

Webhook itu pintu masuk yang terbuka ke server Anda. Email API Anda — MailAnvil, Resend, Mailgun, semua — akan POST payload JSON ke URL yang Anda daftarkan setiap kali ada bounce, complaint, atau delivery. Masalahnya: siapa pun di internet juga bisa POST ke URL itu.

Kalau Anda memproses payload tanpa verifikasi, penyerang bisa menyuntik event palsu. Bayangkan: server Anda menerima email.delivered palsu untuk email yang tidak pernah terkirim, lalu menampilkan "terkirim" ke pelanggan. Atau lebih buruk — event email.complained palsu yang memicu Anda menonaktifkan pengguna yang tidak pernah komplain.

Ada dua pertahanan yang wajib, dan keduanya sederhana.

1. Verifikasi signature HMAC

Setiap webhook MailAnvil dikirim dengan header X-MailAnvil-Signature. Nilainya adalah HMAC-SHA256 dari body mentah (raw bytes, sebelum di-parse jadi JSON), ditandatangani dengan secret unik yang Anda dapat saat membuat webhook.

Kenapa harus HMAC dari body mentah, bukan === biasa? Dua alasan:

  1. Keaslian — hanya pemilik secret yang bisa menghasilkan signature valid. Penyerang yang menebak URL Anda tetap tidak bisa memalsukan event tanpa secret.
  2. Integritas — kalau body berubah satu byte pun di tengah jalan (MITM, proxy, bug), signature tidak cocok dan Anda tahu payload rusak.

Verifikasi di Node.js (Hono/Express/Fastify)

import { createHmac, timingSafeEqual } from 'node:crypto';

app.post('/webhooks/mailanvil', async (req) => {
  const signature = req.headers['x-mailanvil-signature'];
  const rawBody = await req.text(); // HARUS raw string, bukan JSON.parse

  const expected = createHmac('sha256', process.env.MAILANVIL_WEBHOOK_SECRET)
    .update(rawBody)
    .digest('hex');

  // timingSafeEqual, JANGAN pakai ===
  if (!signature || !timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  )) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  // ... proses event
});

Tiga hal yang bikin pola ini benar:

Kalau secret bocor (di-commit ke git, ter-log, ketulis di client), rotasi segera dan anggap semua event lama tidak terpercaya.

2. Cegah SSRF di sisi pengirim

SSRF (Server-Side Request Forgery) adalah sisi lain dari koin yang sama — bukan menyerang endpoint Anda, tapi menyalahgunakan server pengirim. Kalau email API memanggil URL webhook Anda atas nama Anda, penyerang bisa coba daftarkan URL yang menunjuk ke 127.0.0.1, 169.254.169.254 (metadata cloud), atau IP privat lain, lalu memancing server mengakses layanan internal.

API yang serius memvalidasi URL webhook saat dibuat dan saat dikirim. MailAnvil menolak:

Validasi berlapis: sekali saat Anda mendaftarkan URL, dan sekali lagi saat mengirim event (DNS bisa berubah setelah pendaftaran). Defense-in-depth.

Pola lengkapnya

Untuk server yang menerima webhook, checklist keamanan minimal:

  1. Verifikasi HMAC signature dari body mentah (wajib, selalu).
  2. Pakai timingSafeEqual untuk perbandingan secret.
  3. Validasi struktur payload (schema) — jangan percaya event dan data begitu saja.
  4. Log signature yang gagal — itu sinyal seseorang sedang probing endpoint Anda.
  5. Simpan secret di env/secret-manager, jangan di kode.

Webhook email transaksional hanya seaman endpoint yang menerimanya. HMAC + SSRF guard menutup dua celah paling umum. Sisanya adalah disiplin: verifikasi dulu, percaya belakangan.