Bu makalede8 bölüm
- Teknoloji
- Webhook, HMAC-SHA256, RFC 2104, PostgreSQL, Microsoft Graph change notifications
- Konu
- Gelen webhook’ları güvenli, tekrarsız ve sıralı işlemek
- Seviye
- Orta–ileri
- Ön koşullar
- HTTP ve JSON bilgisi
- Temel SQL ve bir sunucu tarafı dil (Python, PHP vb.)
Ödeme kuruluşu, ERP, CRM ya da kargo firması bir olayı webhook ile bildirdiğinde, isteği karşılayan uç nokta entegrasyonun en kırılgan parçasıdır. Sahte isteği reddetmesi, aynı olayı iki kez işlememesi, ters sırada gelen olaylarla veriyi bozmaması ve gönderen sistemi bekletmemesi gerekir. Sahadaki webhook arızalarının çoğu bu dört işten birinin atlanmasından çıkar: imza ayrıştırılmış JSON üzerinden hesaplanır, iş mantığı istek içinde çalışır, tekrar kontrolü yapılmaz.
Webhook alıcısının sorumlulukları
Kurumsal web ve entegrasyon projelerinde webhook alıcısı internete açık bir HTTP uç noktasıdır ve kimlik doğrulaması kullanıcı oturumuna değil, paylaşılan bir sırra dayanır. Doğru kurulmuş bir alıcı şu işleri bu sırayla yapar:
- İsteğin ham gövdesini (raw body) değiştirmeden okur ve boyut sınırını uygular.
- İmzayı ve zaman damgasını doğrular; başarısızsa işleme geçmeden reddeder.
- Olayı tekil kimliğiyle kalıcı bir gelen kutusuna (inbox) yazar; kimlik daha önce görüldüyse yeniden yazmaz.
- Gönderene hızla 2xx döner.
- Asıl iş mantığını (sipariş durumunu güncelleme, ERP’ye aktarma) ayrı bir işçi süreçte çalıştırır.
Bu ayrımın nedeni basittir. Gönderen, yanıtı belirli sürede alamazsa isteği başarısız sayar ve yeniden gönderir. İş mantığı istek içinde çalışırsa yavaş bir ERP çağrısı hem zaman aşımı hem de aynı olayın tekrar gelmesi demektir. Sonuç: aynı sipariş iki kez işlenir. Hangi verinin webhook ile, hangisinin kuyruk veya toplu aktarımla taşınacağı kararı ERP ile web entegrasyon desenleri rehberinde ele alınıyor; burada yalnızca alıcı uç noktaya bakıyoruz.
HMAC imza doğrulama ve zaman damgası kontrolü
Yaygın yöntemde gönderen ile alıcının paylaştığı bir sırla istek gövdesinin HMAC-SHA256 özeti hesaplanır ve bir başlıkta gönderilir. HMAC yapısı RFC 2104 ile tanımlıdır. Sağlayıcılar başlık adında ve imzalanan içerikte farklılaşır:
| Örnek | İmza başlığı | İmzalanan içerik |
|---|---|---|
| GitHub | X-Hub-Signature-256 (sha256= önekiyle) |
Ham gövde |
| Stripe | Stripe-Signature (t= ve v1= alanları) |
Zaman damgası + nokta + ham gövde |
| Standard Webhooks | webhook-signature |
Olay kimliği + zaman damgası + ham gövde |
Kendi sisteminiz başka sistemlere webhook gönderiyorsa, zaman damgasını imzaya dahil eden bir şema seçin; açık bir tarif için Standard Webhooks belirtimi iyi bir başlangıçtır. Zaman damgası imzalanmazsa, ele geçirilmiş geçerli bir istek günler sonra yeniden gönderilebilir (replay saldırısı). Aşağıdaki Python örneği zaman damgasını imzalayan bir şemada doğrulamayı gösterir; başlık adları örnek amaçlıdır.
import hmac, hashlib, time
TOLERANS_SN = 300 # kabul edilen saat farkı
def imza_gecerli_mi(ham_govde: bytes, imza: str, zaman: str, sir: bytes) -> bool:
try:
ts = int(zaman)
except (TypeError, ValueError):
return False
if abs(time.time() - ts) > TOLERANS_SN:
return False # replay penceresi dışında
imzalanan = zaman.encode() + b"." + ham_govde
beklenen = hmac.new(sir, imzalanan, hashlib.sha256).hexdigest()
return hmac.compare_digest(beklenen, imza) # sabit zamanlı karşılaştırma
Bu kodda üç ayrıntı sahada en çok kırılan yerdir:
- Ham gövde kullanılmalı. Çoğu web çatısı JSON gövdeyi otomatik ayrıştırır. Ayrıştırılıp yeniden serileştirilen gövde boşluk ve alan sırası farkı yüzünden aynı baytları üretmez; imza tutmaz. Doğrulamayı çatının ham gövdeye erişim yöntemiyle yapın. “Doğru sır, yanlış imza” şikâyetinin ilk kontrol noktası budur.
- Karşılaştırma sabit zamanlı olmalı.
==ilk farklı karakterde durur ve zamanlama yan kanalı oluşturur; Python’dahmac.compare_digest, PHP’dehash_equalskullanın. - Sunucu saati doğru olmalı. Tolerans penceresi NTP ile senkron bir saat varsayar. Saat birkaç dakika kayarsa tüm istekler reddedilir. Önce
timedatectlçıktısına bakın.
Sır döndürmede (secret rotation) kısa bir geçiş dönemi için iki sırrı birden kabul edin: önce yeni sırrı alıcıya ekleyin, sonra gönderende değiştirin, en son eski sırrı kaldırın. Sırayı ters çevirirseniz geçiş anında gelen olaylar 401 alır. IP izin listesi ek katman olabilir ama imza doğrulamanın yerini tutmaz; bulut sağlayıcıların IP aralıkları değişir.
Hızlı onay, asenkron işleme
İmza geçerliyse alıcı olayı kalıcı bir tabloya yazar ve hemen yanıt döner. Kalıcı yazma yanıttan önce tamamlanmalıdır. Bellekteki bir kuyruğa bırakılıp 200 dönülen olay, süreç yeniden başlarsa kaybolur. Sağlayıcı da onu bir daha göndermez. PostgreSQL için örnek bir gelen kutusu tablosu:
CREATE TABLE webhook_inbox (
event_id text PRIMARY KEY,
source text NOT NULL,
event_type text NOT NULL,
received_at timestamptz NOT NULL DEFAULT now(),
payload jsonb NOT NULL,
status text NOT NULL DEFAULT 'pending',
attempts int NOT NULL DEFAULT 0,
last_error text
);
INSERT INTO webhook_inbox (event_id, source, event_type, payload)
VALUES ($1, $2, $3, $4)
ON CONFLICT (event_id) DO NOTHING;
Yanıt kodlarını bilinçli seçin. Geçersiz imzaya 401 veya 403, bozuk gövdeye 400 dönün. Kayıt başarılıysa ya da olay zaten varsa 200 veya 202 dönün. Veritabanına yazılamıyorsa 5xx doğrudur; gönderen yeniden dener. İş mantığındaki bir hatayı (örneğin ERP’de ürün bulunamadı) ise 5xx ile göndericiye yansıtmayın. Bu hata yeniden denemeyle düzelmez; gönderenin kuyruğunu boşuna meşgul eder ve bazı sağlayıcılarda uç noktanın devre dışı bırakılmasına kadar gider. Bu tür hatalar işçi tarafında status ve last_error alanlarıyla yönetilir.
İşçi süreç pending kayıtları alır; PostgreSQL’de birden fazla işçinin aynı kaydı almasını önlemek için SELECT … FOR UPDATE SKIP LOCKED yaygın yöntemdir. Belirli sayıda başarısız denemeden sonra kayıt ayrı bir duruma alınır ve insan incelemesine düşer. Deneme politikasının ayrıntıları API hata yönetimi ve retry rehberinde anlatılıyor.
Tekrar eden olaylar ve idempotent işleme
Yaygın webhook sağlayıcıları “en az bir kez” (at-least-once) teslim modeliyle çalışır; tek seferlik teslim garantisi vermez. Ağ kesintisi, zaman aşımı ya da alıcının yanıtının gönderene ulaşmaması aynı olayın birden fazla gelmesine yol açar. Tekrar olağandır. Savunma iki katmanlıdır:
- Olay kimliğiyle eleme: Yukarıdaki tablodaki
event_idbirincil anahtarı aynı olayın ikinci kez yazılmasını engeller. Olay kimliği yoksa kaynak, olay türü, kaynak kayıt kimliği ve olay zamanından türetilen bir özet anahtar kullanılabilir. Bu ikinci tercihtir: aynı saniyede gerçekten iki farklı olay oluşursa çakışabilir. - İşlemin kendisini idempotent yapmak: “Stok 5 azalt” yerine “stok 37 olsun”, “ödeme ekle” yerine “bu ödeme kimliği yoksa ekle” biçimindeki işlemler tekrar geldiğinde sonucu değiştirmez. Olay kimliği tablosu bir gün temizlense bile ikinci savunma çalışır.
Gelen kutusu tablosu sınırsız büyümemeli. Sağlayıcının en uzun yeniden deneme süresinden uzun bir saklama süresi (örneğin birkaç hafta) belirleyin ve eski kayıtları periyodik olarak silin.
Sıra dışı olaylar ve sürüm alanı
“Sipariş kargoya verildi” olayı “sipariş onaylandı” olayından önce gelebilir. Olayları geliş sırasıyla uygulayan bir alıcı siparişi “kargoda” durumundan “onaylandı” durumuna geri çeker; müşteri paneli geriye gider, destek hattı çalar. Üç yaklaşım vardır:
- Sürüm veya sıra numarasıyla koşullu güncelleme: Olay kaynak kaydın sürümünü ya da değişiklik zamanını taşıyorsa, yalnızca daha yeni sürüm uygulanır.
- Durum makinesiyle geçiş kontrolü: İzin verilen geçişler tanımlanır; “kargoda”dan “onaylandı”ya geçiş reddedilir ve olay yok sayılır.
- İnce olay + yeniden okuma: Olay yalnızca “şu kayıt değişti” bilgisini taşır; alıcı kaydın güncel hâlini kaynağın API’sinden okur. Sıra sorunu ortadan kalkar, karşılığında her olay bir API çağrısına mal olur.
UPDATE orders
SET status = $1,
source_version = $2,
updated_at = now()
WHERE external_id = $3
AND source_version < $2; -- eski sürüm gelirse 0 satır güncellenir
Güncellenen satır sayısı sıfırsa olay ya eskidir ya da kayıt henüz yoktur. İkinci durumu ayırmak için kaydın varlığını kontrol edin. Kayıt yoksa olayı kısa bir gecikmeyle yeniden kuyruğa almak, sessizce düşürmekten daha güvenlidir.
Doğrulama el sıkışmaları: Microsoft Graph örneği
Bazı sağlayıcılar abonelik oluşturulurken uç noktanın gerçekten size ait olduğunu doğrular. Microsoft Graph change notifications, abonelik oluşturulurken uç noktaya validationToken sorgu parametresi içeren bir istek gönderir ve bu değerin düz metin olarak kısa süre içinde geri dönülmesini bekler. Her bildirimde ayrıca abonelikte belirlediğiniz clientState değeri gelir; alıcı bu değeri karşılaştırarak bildirimin kendi aboneliğine ait olduğunu doğrular. Sahada sık gördüğümüz hata, alıcının bu ilk isteği de imza kontrolüne sokup reddetmesi ve aboneliğin hiç oluşmamasıdır. Graph tarafındaki uygulama izinlerinin nasıl daraltılacağı Microsoft Graph API izinleri rehberinde anlatılıyor.
Test, yeniden oynatma ve izleme
Alıcı, üretimde ilk olay gelene kadar test edilmemiş kalmamalı. Devreye almadan önce ve her önemli değişiklikte şu listeyi geçin:
- Geçerli imzalı istek kabul ediliyor, gövdede tek bayt değiştirildiğinde reddediliyor mu?
- Tolerans penceresi dışındaki zaman damgası reddediliyor mu?
- Aynı olay iki kez gönderildiğinde tek kayıt oluşuyor ve ikinci istek de 2xx alıyor mu?
- Ters sırada gönderilen iki olayda son durum doğru mu?
- Veritabanı erişilemezken alıcı 5xx dönüyor ve sağlayıcı yeniden deniyor mu?
- Büyük gövde sınırı ve yalnız HTTPS erişimi uygulanıyor mu?
- Sağlayıcı panelinden geçmiş bir olayı yeniden gönderme (redelivery) özelliği biliniyor ve deneniyor mu?
Üretimde en az şu göstergeleri izleyin: dakika başına alınan olay sayısı, imza hatası oranı, pending durumundaki en eski olayın yaşı ve başarısız duruma düşen olay sayısı. Beklenen saatlerde olay sayısının sıfıra düşmesi çoğu zaman hata oranındaki artıştan daha önemli bir sinyaldir. Sessizlik de arızadır. Bu iş seviyesi izlemenin kurgusu entegrasyon izleme ve mutabakat rehberinde yer alıyor. Webhook ve API tabanlı entegrasyon projelerinde kapsam için ERP, CRM ve API entegrasyonları sayfamıza göz atabilirsiniz.
Sık sorulan sorular
Webhook imza doğrulama için IP izin listesi yeterli mi?
Hayır. IP izin listesi isteğin hangi ağdan geldiğini sınırlar; içeriğin değiştirilmediğini veya isteğin yeniden oynatılmadığını kanıtlamaz. Sağlayıcıların IP aralıkları da zamanla değişir. Temel kontrol imza doğrulamadır; IP listesi varsa yalnız ek katman olarak kalır.
İmza neden bazen doğru sırra rağmen tutmuyor?
En sık neden, imzanın ham gövde yerine çatının ayrıştırıp yeniden serileştirdiği JSON üzerinden hesaplanmasıdır. Boşluk, alan sırası veya Unicode kaçışı değişince özet de değişir. Diğer nedenler yanlış karakter kodlaması, başlıktaki sha256= gibi öneklerin ayıklanmaması ve sunucu saatinin kaymasıdır.
Webhook alıcısı hata durumunda hangi HTTP kodunu dönmeli?
Geçersiz imzaya 401 veya 403, bozuk isteğe 400 dönülür. Olay kalıcı olarak kaydedildiyse ya da daha önce kaydedilmişse 200 veya 202 dönülür. 5xx yalnız kayıt aşamasındaki geçici altyapı hatalarında kullanılır; iş mantığı hataları gönderene yansıtılmaz, alıcı tarafında yönetilir.
Olay kimliği tablosunda kayıtlar ne kadar süre tutulmalı?
Sağlayıcının belgelediği en uzun yeniden deneme süresinden daha uzun tutulmalıdır. Pratikte birkaç haftalık saklama çoğu senaryoda yeter. İşlemler ayrıca idempotent tasarlandıysa, eski kayıtlar temizlendikten sonra gelen nadir bir tekrar da veriyi bozmaz.
- Teknoloji
- Webhook, HMAC-SHA256, RFC 2104, PostgreSQL, Microsoft Graph change notifications
- Konu
- Gelen webhook’ları güvenli, tekrarsız ve sıralı işlemek
- Seviye
- Orta–ileri
- Ön koşullar
- HTTP ve JSON bilgisi
- Temel SQL ve bir sunucu tarafı dil (Python, PHP vb.)
Entegrasyon ve Bakım