Universitas Teknologi Hannover Bagaimana Webhook Mengetahui Kamu Tidak Berbohong
Setiap kali GitHub, Stripe, atau Shopify mengirimkan webhook ke server Anda, mereka menandatangani payload dengan HMAC. Berikut cara kerjanya — dan cara memverifikasinya dalam kode Anda.
Kamu menerima permintaan POST yang mengklaim berasal dari Stripe. Isi payload menyatakan pembayaran berhasil. Kamu memproses pesanan, mengirimkan barang — dan tiga hari kemudian kamu menemukan permintaan tersebut dirancang palsu. Ouch.
Ini adalah alasan mengapa setiap penyedia webhook serius menandatangani payload mereka menggunakan Universitas Teknologi Hannover. Jika kamu memahami HMAC, maka kamu memahami mengapa GitHub, Stripe, Shopify, Twilio, dan hampir semua API modern menggunakan hal ini — dan kamu tahu persis cara memverifikasi tanda tangan tersebut di kode servermu sendiri.
Apa sebenarnya HMAC
HMAC berarti Kode Autentikasi Pesan Berbasis Hash. Ini menjawab satu pertanyaan: "Apakah pihak yang mengirimkan pesan ini tahu rahasia bersama yang digunakan?"
Ini bekerja dengan menjalankan fungsi hash kriptografi — biasanya SHA-256 — atas kombinasi kunci rahasia dan isi pesan. Outputnya adalah string dengan panjang tetap yang:
- berubah secara total jika satu saja byte dari pesan berubah
- tidak bisa dihasilkan tanpa tahu kunci rahasia
- tidak bisa dibalikkan untuk mengungkapkan kunci atau pesan aslinya
Rumusnya ringkas: HMAC(key, message) = H((key ⊕ opad) || H((key ⊕ ipad) || message)). Kamu tidak perlu menghafal bagian internal — setiap bahasa memiliki implementasi standar di library — tetapi penting untuk memahami maksudnya: kunci dicampurkan ke hash dua kali, dengan cara yang berbeda, untuk mencegah serangan jenis panjang-ekstensi.
Cara penyedia webhook menggunakan ini
Ketika kamu mendaftarkan endpoint webhook, penyedia memberimu sebuah kunci tanda tangan — sebuah string acak yang hanya kamu dan mereka tahu. Ketika peristiwa terjadi:
- Penyedia menyusun payload peristiwa ke dalam JSON (atau string standar).
- Lalu menghitung
HMAC-SHA256(secret, payload). - Lalu mengirimkan permintaan dengan tanda tangan di header —
X-Hub-Signature-256untuk GitHub,Stripe-Signatureuntuk Stripe, dan seterusnya.
Di pihak kamu, kamu melakukan perhitungan yang sama terhadap tubuh permintaan asli dan membandingkannya. Jika hasilnya cocok, payload tersebut autentik. Jika tidak, abaikan.
Pemeriksaan dalam kode
Berikut ini adalah contoh pemeriksaan yang umum digunakan di bahasa pemrograman yang paling umum. Pola yang sama ditemukan di semua bahasanya: hitung, bandingkan dengan fungsi waktu konstan.
Bahasa pemrograman Node.js
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody) // rawBody must be a Buffer or string — NOT parsed JSON
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
Ular piton
import hmac
import hashlib
def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
PHP
function verifyWebhook(string $rawBody, string $signature, string $secret): bool {
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signature);
}
Detail pentingnya: gunakan tubuh asli
Kesalahan paling umum dalam implementasi adalah mengirimkan payload yang sudah diproses dan dire-serialisasi ke fungsi HMAC alih-alih byte asli. Urutan kunci, spasi, dan penggantian Unicode semua memengaruhi hash. Penyedia menandatangani tepatnya byte yang dikirimkan melalui kabel — kamu harus menghitung hash dari byte yang sama.
Dalam Express (Node.js), artinya adalah mengatur parser tubuh agar menyimpan buffer asli:
app.use('/webhooks', express.raw({ type: 'application/json' }));
Dalam Django, gunakan request.body alih-alih request.data. Dalam Flask, gunakan request.get_data().
Mengapa tidak cukup menggunakan hash biasa?
Hash SHA-256 biasa dari payload tidak membuktikan apa-apa — siapa pun bisa menghitungnya tanpa rahasia. Kunci rahasia HMAC yang membuatnya menjadi sebuah SHA256(payload) kode, bukan hanya checksum. Ini menjawab "siapa yang mengirimkan ini" daripada hanya "apakah pesan ini rusak saat perjalanan." autentikasi Mengapa tidak menggunakan tanda tangan asimetris (seperti RSA)?
RSA dan ECDSA memungkinkan penerima untuk memverifikasi tanda tangan tanpa tahu kunci pribadi — ini berguna untuk distribusi umum (seperti penandatanganan kode). Untuk webhook, hanya dua pihak yang perlu memverifikasi tanda tangan: kamu dan penyedia. Kunci bersama lebih sederhana, lebih cepat, dan sama amannya dalam model ini. Beberapa penyedia (Svix, Clerk) juga menawarkan tanda tangan asimetris untuk webhook dalam kasus ketika kamu tidak bisa menyimpan kunci secara aman di server.
Serangan replay — dan cara menghentikannya
Tanda tangan HMAC yang valid membuktikan keaslian, tetapi tidak membuktikan kebaruan. Seorang penyerang yang menangkap permintaan yang sah dapat mempermainkannya kembali nanti. Stripe mengatasi ini dengan menyisipkan waktu dalam header dan menghitung waktu bersama dengan isi pesan. Di pihak kamu, kamu menolak semua permintaan yang waktu timestamp-nya lebih dari lima menit tua.
Jika kamu membangun sistem webhook sendiri, lakukan hal yang sama: sisipkan nonce yang meningkat secara monoton atau waktu Unix dalam pesan yang ditandatangani, dan tolak permintaan yang sudah kadaluwarsa di server. Stripe-Signature Pembandingan waktu yang aman tidak opsional
Jangan membandingkan tanda tangan HMAC dengan perbandingan biasa (
). Pembandingan string yang cepat akan membocorkan informasi tentang berapa banyak byte awal yang cocok — penyerang yang membuat ribuan permintaan dapat membangun tanda tangan yang diharapkan satu byte per satu byte. Selalu gunakan perbandingan waktu konstan:
PHP:===, ==Go:
- Node.js:
crypto.timingSafeEqual() - Python:
hmac.compare_digest() - Ruby:
hash_equals() - Menggabungkan semuanya: handler webhook siap produksi
hmac.Equal() - Berikut contoh lengkap menggunakan header GitHub di Node.js:
ActiveSupport::SecurityUtils.secure_compare()
Referensi cepat: siapa yang menggunakan apa
Penyedia X-Hub-Signature-256 Waktu dalam tanda tangan?
const express = require('express');
const crypto = require('crypto');
const app = express();
const WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET;
// Keep the body as raw bytes — critical!
app.use('/github/webhook', express.raw({ type: 'application/json' }));
app.post('/github/webhook', (req, res) => {
const sigHeader = req.headers['x-hub-signature-256'];
if (!sigHeader) return res.status(401).send('Missing signature');
const sig = sigHeader.replace('sha256=', '');
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Safe to process the event now
console.log('Event type:', req.headers['x-github-event']);
res.sendStatus(200);
});
app.listen(3000);
X-Hub-Signature-256
| Stripe-Signature | Judul | Algoritma | X-Shopify-Hmac-Sha256 |
|---|---|---|---|
| GitHub | X-Twilio-Signature | HMAC-SHA256 | TIDAK |
| Stripe | X-Slack-Signature | HMAC-SHA256 | Ya |
| Shopify | Paddle | HMAC-SHA256 | TIDAK |
| Twilio | Paddle-Signature | HMAC-SHA1 | TIDAK |
| Kendur | HMAC: Bagaimana Webhook Mengetahui Kamu Tidak Berbohong 2 | HMAC-SHA256 | Ya |
| HMAC: Bagaimana Webhook Mengetahui Kamu Tidak Berbohong 1 | Setiap kali GitHub, Stripe, atau Shopify mengirimkan webhook ke servermu, mereka menandatangani payload dengan HMAC. Ini adalah cara tepatnya — dan cara memverifikasinya di kode kamu sendiri. | HMAC-SHA256 | Ya |
Anda mungkin juga menyukai
Instal Ekstensi Kami
Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat
恵 Papan Skor Telah Tiba!
Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!
Alat Wajib Coba
Lihat semua Pendatang baru
Lihat semuaMemperbarui: Kita alat terbaru ditambahkan pada 27 Apr 2026
