Tidak suka iklan? Pergi Bebas Iklan Hari ini

Tanda Tanda Webhook Verifikasi Isi dan Hentikan Permintaan Palsu

Diperbarui pada

Pelajari cara tanda tangan webhook HMAC-SHA256 berfungsi, cara mengimplementasikan verifikasi waktu konstan dalam Python, Node.js, dan PHP, serta kesalahan umum yang menyebabkan kegagalan diam-diam di produksi.

Tanda Tanda Webhook: Verifikasi Payload dan Hentikan Permintaan Palsu 1
IKLAN · HAPUS?

Siapa saja dapat mengirimkan permintaan POST ke titik akhir webhook Anda. Tanpa verifikasi tanda tanda, server Anda tidak memiliki cara untuk mengetahui apakah permintaan tersebut benar-benar berasal dari Stripe, GitHub, atau infrastruktur Anda sendiri — atau dari penyerang yang merekam dan mengulang peristiwa yang sah.

Verifikasi tanda tanda webhook menyelesaikan masalah ini. Ini adalah cara prosesor pembayaran, platform kontrol versi, dan sistem e-commerce memungkinkan Anda membuktikan keaslian sebelum bertindak terhadap data yang masuk. Artikel ini menjelaskan bagaimana tanda tanda HMAC-SHA256 bekerja, pola verifikasi langkah demi langkah, serta kesalahan halus yang menyebabkan kegagalan di produksi.

Mengapa Webhook Tidak Diverifikasi Adalah Risiko Keamanan

Titik akhir webhook hanyalah sebuah penangani HTTP yang diunggah ke internet. Tanpa verifikasi, setiap permintaan yang mencapai titik akhir tersebut akan diproses. Dua serangan yang menonjol adalah:

  • Palsu — penyerang membuat payload palsu yang terlihat seperti peristiwa sah (sebuah pembayaran berhasil, langganan diperpanjang) dan memicu tindakan di sisi Anda tanpa terjadi transaksi nyata.
  • Serangan ulang — permintaan sah yang dikirimkan dalam perjalanan diambil dan dikirim kembali nanti. Jika titik akhir Anda idempoten tetapi tidak dilindungi, peristiwa yang sama akan diaktifkan beberapa kali.

Kedua serangan ini dapat dicegah dengan rahasia bersama yang dikombinasikan dengan tanda tanda kriptografi. Pengirim menandai payload; Anda memverifikasi tanda tanda sebelum memproses apa pun.

Cara Tanda Tanda HMAC-SHA256 Bekerja

HMAC (Kode Autentikasi Pesan Berbasis Hash) menerima dua input: sebuah kunci rahasia dan sebuah pesan. Ia menjalankan keduanya melalui fungsi hash — SHA-256 dalam sebagian besar implementasi webhook — dan menghasilkan tanda tanda dengan panjang tetap.

Sifat kunci: dengan kunci rahasia yang sama dan pesan yang sama, tanda tanda selalu dihasilkan sama, dan perubahan bahkan satu byte dalam pesan menghasilkan output yang sama sekali berbeda. Siapa pun yang tidak memiliki kunci rahasia tidak dapat menghasilkan tanda tanda yang valid, bahkan jika mereka dapat melihat seluruh payload.

Dalam praktiknya, alur ini terlihat seperti ini:

  1. Anda mendaftar webhook Anda dengan layanan (misalnya Stripe). Layanan tersebut memberikan Anda sebuah kunci tanda tangan.
  2. Ketika layanan mengirimkan peristiwa, ia menghitung HMAC-SHA256(secret, payload) dan mencantumkan hasilnya dalam header permintaan.
  3. Server Anda menerima permintaan tersebut, menghitung HMAC yang sama menggunakan kunci rahasia yang disimpan dan tubuh permintaan mentah, lalu membandingkan kedua tanda tanda tersebut.
  4. Jika mereka cocok, permintaan tersebut autentik. Jika tidak, tolak permintaan tersebut.

Polanya Verifikasi, Langkah demi Langkah

Berikut adalah implementasi Python yang mencerminkan apa yang diharapkan oleh setiap layanan utama:

import hmac
import hashlib

def verify_signature(payload: bytes, secret: str, received_sig: str) -> bool:
    expected = hmac.new(
        key=secret.encode("utf-8"),
        msg=payload,
        digestmod=hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, received_sig)

Dua hal penting di sini selain panggilan HMAC itu sendiri. Pertama: payload adalah bytes, bukan string yang telah didekodkan — Anda harus melewati tubuh permintaan mentah tepat seperti diterima dari jaringan. Kedua: perbandingan menggunakan hmac.compare_digest alih-alih ==. Ini sengaja dilakukan.

Mengapa Perbandingan Waktu Konstan Tidak Wajib

Perbandingan string dalam kebanyakan bahasa pendekatan secara cepat: ia mengembalikan false saat menemukan karakter yang tidak cocok. Seorang penyerang yang dapat mengukur waktu respons dapat memanfaatkannya — mengirim ribuan permintaan dengan tanda tanda yang bervariasi dan menggunakan perbedaan waktu untuk menebak nilai yang benar karakter per karakter. Ini adalah serangan waktu.

hmac.compare_digest dalam Python, hash_equals dalam PHP, dan crypto.timingSafeEqual dalam Node.js semua membutuhkan waktu yang sama terlepas dari tempat string berbeda. Gunakan mereka setiap kali Anda membandingkan tanda tanda — tidak ada pengecualian.

Format Header Nyata: Stripe, GitHub, Shopify

Setiap layanan memiliki nama header dan pengkodean tanda tanda yang sedikit berbeda. Berikut yang harus Anda parse untuk tiga integrasi paling umum.

Stripe

Stripe mengirimkan header Stripe-Signature dengan format ini:

Stripe-Signature: t=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a05bd539e6d5b9d2a2d2fbe

Itu t field adalah waktu Unix saat peristiwa dikirim. Nilai v1 adalah HMAC-SHA256 dari timestamp + "." + raw_body. Saat mengimplementasikan secara manual:

  1. Parse header untuk mengekstrak t dan v1.
  2. Hitung HMAC-SHA256(secret, t + "." + raw_body).
  3. Bandingkan dengan v1 menggunakan perbandingan waktu konstan.
  4. Tolak jika t lebih dari 300 detik (5 menit) dari waktu saat ini.

GitHub

GitHub menggunakan X-Hub-Signature-256 :

X-Hub-Signature-256: sha256=6ffbb59b2300aae63f272406069a9788598b770f698a48021f99b32f8de06bb3

Hapus sha256= prefiks, lalu bandingkan sisa bagian dengan HMAC Anda dari tubuh mentah. GitHub tidak mengintegrasikan waktu dalam konten yang ditandai, sehingga implementasikan deduplikasi peristiwa secara terpisah jika perlindungan ulang penting bagi Anda.

Shopify

Shopify menggunakan X-Shopify-Hmac-Sha256 dengan digest yang dienkripsi Base64 alih-alih heksa:

X-Shopify-Hmac-Sha256: b6LPiZidmXnJQf0Ff/p7MZQPIBPN9TqAqLMgAfn2YLQ=

Dekode kedua HMAC yang dihitung dan nilai header dari Base64 ke byte mentah, lalu bandingkan dengan fungsi waktu konstan. Membandingkan string Base64 secara langsung dapat memperkenalkan bug halus dalam normalisasi enkoding.

Kesalahan Paling Umum: Tubuh Mentah vs. JSON yang Didekode

Ini adalah tempat kegagalan verifikasi tanda tanda webhook terjadi di produksi. Ketika kerangka kerja Anda secara otomatis mendekode tubuh permintaan ke dalam dict atau objek sebelum handler berjalan, byte mentahnya hilang. HMAC dihitung terhadap byte-nya yang awal — bukan terhadap apa pun yang dihasilkan oleh library JSON saat mengubah struktur yang sudah didekode.

Bahkan ketika data secara semantik identik, {"amount": 100} dan {"amount":100} (tidak ada spasi setelah tanda koma) menghasilkan nilai HMAC yang berbeda. Perbedaan urutan field, normalisasi Unicode, dan presisi float dapat secara diam-diam menghancurkan verifikasi tanpa kesalahan yang jelas.

Solusi: buffer tubuh permintaan mentah sebelum parsing terjadi dan lewatkan byte-nya ke fungsi verifikasi Anda. Dalam Express.js, daftarkan rute dengan express.raw() middleware alih-alih express.json():

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const rawBody = req.body; // Buffer — not a parsed object
  const sig = req.headers['stripe-signature'];
  // Verify rawBody against sig before JSON.parse()
});

Dalam Django, gunakan request.body (byte). Dalam Laravel, gunakan $request->getContent(). Pola ini konsisten: akses byte mentah secara langsung dari permintaan, tidak pernah meng-serialize representasi yang sudah didekode.

Perlindungan Ulang dengan Waktu

Tanda tanda yang valid hanya membuktikan bahwa pesan tidak dimodifikasi saat perjalanan. Ini tidak mencegah penyerang menangkap permintaan sah dan mengirimkannya kembali beberapa jam kemudian — tanda tanda tetap akan diverifikasi dengan benar karena tidak ada perubahan.

Stripe menangani ini dengan mencantumkan waktu Unix dalam pesan yang ditandai dan merekomendasikan jendela toleransi 5 menit. Setelah mengekstrak t dari Stripe-Signature header, tolak permintaan jika waktu tersebut berada di luar jendela tersebut:

import time

def is_within_tolerance(timestamp: int, tolerance_seconds: int = 300) -> bool:
    return abs(time.time() - timestamp) <= tolerance_seconds

# In your handler:
if not is_within_tolerance(stripe_timestamp):
    return HttpResponse(status=403)  # Reject stale request

Untuk layanan seperti GitHub dan Shopify yang tidak mengintegrasikan waktu dalam skema tanda tanda, implementasikan perlindungan ulang dengan menyimpan ID peristiwa yang sudah diproses (tersedia dalam payload) dan menolak ID mana pun yang sudah Anda tangani. Cache pendek atau set Redis dengan TTL yang sesuai dengan jendela pemrosesan bekerja dengan baik.

Verifikasi Tanda Tanda Tanpa Menulis Kode

Saat memperbaiki integrasi webhook — atau memeriksa payload yang sudah Anda terima — Validator Tanda Tangan Webhook mengizinkan Anda untuk menempelkan payload, kunci rahasia, dan tanda tanda yang diterima untuk memeriksa verifikasi secara instan, tanpa harus membangun lingkungan lokal.

Untuk menghasilkan tanda tanda HMAC untuk pengujian endpoint dengan payload yang dirancang, gunakan Pembangkit HMAC. Ini menghasilkan output hex dan Base64 untuk SHA-256, SHA-512, dan beberapa algoritma digest lainnya — berguna untuk membangun kasus uji yang mencakup format yang digunakan oleh setiap layanan.

Rincian Singkat

  • Skema tanda tanda HMAC-SHA256 dengan kunci bersama adalah skema tanda tanda standar di Stripe, GitHub, Shopify, dan kebanyakan layanan lainnya.
  • Gunakan perbandingan waktu konstan (hmac.compare_digest, hash_equals, crypto.timingSafeEqual) — bukan ==.
  • Lewati byte tubuh permintaan mentah ke fungsi HMAC Anda, tidak pernah objek yang di-serialize ulang.
  • Periksa waktu pada layanan yang mencantumkan waktu; jendela toleransi Stripe adalah 5 menit.
  • Deduplikasi dengan ID peristiwa untuk layanan tanpa perlindungan ulang berbasis waktu.
  • Nama header dan pengkodean (hex vs Base64) berbeda antar layanan — selalu periksa dokumen integrasi.
Ingin bebas iklan? Bebas Iklan Hari Ini

Instal Ekstensi Kami

Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat

Ke Ekstensi Chrome Ke Ekstensi Tepi Ke Ekstensi Firefox Ke Ekstensi Opera

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!

IKLAN · HAPUS?
IKLAN · HAPUS?
IKLAN · HAPUS?

Pojok Berita dengan Sorotan Teknologi

Terlibat

Bantu kami untuk terus menyediakan alat gratis yang berharga

Belikan aku kopi
IKLAN · HAPUS?