API Tasarımı & Event-Driven Entegrasyon 3 dk okuma 9 okunma

Webhook Tasarımı: Güvenlik, İmzalama ve Retry Mekanizması

Webhook'lar, bir olay gerçekleştiğinde sistemlerin birbirine otomatik HTTP bildirimi göndermesini sağlar. Güvenli ve güvenilir bir webhook sistemi için HMAC imzalama, timestamp ile replay saldırı koruması, idempotency, exponential backoff ile retry, dead-letter queue ve HTTPS zorunluluğu kritiktir. Bu yazıda, üretim kalitesinde bir webhook altyapısının tasarımını ele alıyoruz.

Webhook Tasarımı: Güvenlik, İmzalama ve Retry Mekanizması

Webhook Tasarımı: Güvenlik, İmzalama ve Retry Mekanizması

Webhook'lar, modern sistemlerin gerçek zamanlı iletişiminin temelidir: bir ödeme tamamlandığında, bir sipariş oluşturulduğunda veya bir dosya yüklendiğinde, ilgili sistemlere otomatik bildirim gönderilir. Ancak "payload gönder ve unut" yaklaşımı, üretim ortamında güvenlik açıklarına, kayıp bildirimlere ve tekrarlanan işlemlere yol açar. Bu yazıda, güvenli ve güvenilir bir webhook altyapısının nasıl tasarlanacağını ele alacağız.


1. Temel Akış ve Bileşenler

Tipik bir webhook akışı şu adımlardan oluşur:

  1. Olay gerçekleşir: Kaynak sistemde bir değişiklik olur (ör. order.created).

  2. Aboneler bulunur: Bu olayı dinleyen kayıtlı aboneler tespit edilir.

  3. Payload hazırlanır: Olay verisi JSON formatında paketlenir.

  4. İmzalanır: Payload, paylaşılan bir gizli anahtarla HMAC kullanılarak imzalanır.

  5. HTTP POST gönderilir: Abonenin webhook URL'sine iletilir.

  6. Abone doğrular ve işler: İmza kontrol edilir, payload işlenir.

  7. Retry yönetimi: Başarısız olursa, politika ile yeniden denenir.


2. Güvenlik: HMAC İmzalama ve Replay Koruması

A. HMAC İmzalama (En Kritik Katman)

Webhook endpoint'leri halka açık URL'lerdir; kötü niyetli kişiler sahte istekler gönderebilir. HMAC-SHA256 imzası, isteğin gerçekten sizin sisteminizden geldiğini doğrulamanın standart yoludur.

Gönderen Taraf:

csharp

var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var signedPayload = $"{timestamp}.{payload}";

using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var signature = Convert.ToHexString(
    hmac.ComputeHash(Encoding.UTF8.GetBytes(signedPayload))).ToLowerInvariant();

request.Headers.Add("X-Webhook-Signature", signature);
request.Headers.Add("X-Webhook-Timestamp", timestamp.ToString());

Alıcı Taraf:

csharp

var timestamp = Request.Headers["X-Webhook-Timestamp"].ToString();
var signature = Request.Headers["X-Webhook-Signature"].ToString();

// 1. Timestamp kontrolü (5 dakikadan eski istekleri reddet)
var requestTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(timestamp));
if (DateTimeOffset.UtcNow - requestTime > TimeSpan.FromMinutes(5))
    return Unauthorized("İstek çok eski.");

// 2. İmza doğrulama (timing-safe karşılaştırma!)
var expected = ComputeSignature(payload, secret, timestamp);
if (!CryptographicOperations.FixedTimeEquals(
    Encoding.UTF8.GetBytes(signature),
    Encoding.UTF8.GetBytes(expected)))
    return Unauthorized("Geçersiz imza.");

Kritik Detay: CryptographicOperations.FixedTimeEquals kullanmak, zamanlama saldırılarını (timing attacks) önler. Normal == karşılaştırması, karakter karakter karşılaştırma yaptığı için saldırgana bilgi sızdırabilir.

B. Timestamp ve Replay Attack Koruması

Timestamp, aynı webhook'un yakalanıp tekrar gönderilmesini (replay attack) engeller. Sunucu, belirli bir süreden (genellikle 5 dakika) eski istekleri reddeder.

C. HTTPS Zorunluluğu

Webhook URL'leri her zaman HTTPS olmalıdır. HTTP üzerinden gönderilen webhook'lar, araya giren saldırganlar tarafından okunabilir ve değiştirilebilir.

D. Secret Yönetimi

Her abone için benzersiz bir secret oluşturulmalı; veritabanında şifrelenmiş saklanmalı ve düzenli olarak döndürülmelidir.


3. Idempotency: Aynı Webhook'un Tekrar İşlenmesini Engelleme

Webhook'lar at-least-once teslim edilir; aynı olay birden fazla kez gelebilir. Abone, işlemini idempotent yapmalıdır.

Çözüm: Her webhook olayına benzersiz bir webhook_id (UUID) atanır. Abone, bu ID'yi Redis veya veritabanında saklayarak aynı olayın tekrar işlenmesini engeller.

csharp

if (await _redis.KeyExistsAsync($"processed:{webhookId}"))
    return Ok(); // Zaten işlenmiş

// İşle...
await _redis.StringSetAsync($"processed:{webhookId}", "1", TimeSpan.FromDays(7));

4. Retry Mekanizması: Güvenilir Teslimat

A. Hangi Durumlarda Retry Yapılmalı?

Durum Kodu Retry? Açıklama
2xx ❌ Başarılı
400, 401, 404 ❌ Kalıcı hata; retry faydasız
408, 429 ✅ Geçici hata; Retry-After'a uyulmalı
5xx ✅ Sunucu hatası; geçici olabilir
Bağlantı hatası ✅ Ağ sorunu

B. Exponential Backoff + Jitter

Retry denemeleri arasındaki bekleme süresi katlanarak artmalı ve rastgele bir sapma (jitter) eklenmelidir:

    1. deneme: hemen

    1. deneme: ~10 saniye

    1. deneme: ~1 dakika

    1. deneme: ~10 dakika

    1. deneme: ~1 saat

Jitter, aynı anda binlerce webhook'un aynı anda yeniden denenmesini (thundering herd) önler.


5. Dead Letter Queue (DLQ)

Tüm retry denemeleri başarısız olduğunda, webhook bir DLQ'ya gönderilmelidir. Bu, olayın kaybolmasını önler ve manuel inceleme imkanı tanır. DLQ'daki olaylar, bir yönetim panelinden yeniden gönderilebilmelidir.


6. En İyi Pratikler

Pratik Açıklama
HMAC-SHA256 ile imzalayın Her webhook'u paylaşılan secret ile imzalayın.
Timestamp kullanın Replay attack'ları önlemek için 5 dakikalık pencere.
Idempotency sağlayın webhook_id ile tekrar işlemeyi engelleyin.
Exponential backoff + jitter Retry'larda akıllı bekleme süreleri.
DLQ kullanın Başarısız webhook'ları kaybetmeyin.
Hızlı yanıt dönün Abone, işlemi arka planda yapıp hemen 200 OK dönmeli.
İzleme kurun Teslimat başarı oranı ve gecikmeyi takip edin.

Sonuç

Webhook'lar, modern sistemlerin gerçek zamanlı omurgasıdır; ancak güvenli ve güvenilir hale getirmek ciddi bir mühendislik gerektirir. HMAC imzalama, timestamp, idempotency, exponential backoff, DLQ ve HTTPS gibi pratikleri uygulayarak, olayların güvenli bir şekilde iletilmesini ve kaybolmamasını garanti edebilirsiniz. Unutmayın: Webhook, bir sistemin diğerine verdiği sözdür; bu sözü tutmak, iyi bir mühendislik gerektirir.

Tüm yazılar Soru ya da yorumun mu var? Yaz