DEVekspertiz.app

01 · Kimlik Doğrulama (HMAC İmzalama)

Partner API, HMAC-SHA256 imzalı request'ler kullanır (AWS SigV4 ve Stripe Webhooks ile aynı yaklaşım). Secret asla ağda gitmez; her istek için yeniden hesaplanan bir imza taşınır. Timestamp ve nonce zorunluluğu replay saldırılarını engeller.

Gereksinimler

Her isteğe aşağıdaki 4 header zorunludur:

HeaderFormatAçıklama
X-Trameks-Key-Idtmk_live_… / tmk_test_…Public key ID (log'larda görünebilir)
X-Trameks-Timestamp1715900400UNIX saniye (UTC). ±5 dakika tolerans
X-Trameks-Nonce16+ karakter hex/base32İstek başına benzersiz, replay koruması için
X-Trameks-Signaturesha256=<64 hex>Aşağıdaki kanonik string'in HMAC'i

Opsiyonel:

  • X-Request-Id — istek başına UUID; bizim log'larımız da bu ID ile eşlenir.

Kanonik string

Tüm parçalar \n (line feed, 0x0A) ile birleştirilir:

METHOD\n
PATH\n
CANONICAL_QUERY\n
TIMESTAMP\n
NONCE\n
BODY_SHA256

METHOD

HTTP metodu, büyük harf: GET, POST, PUT, DELETE.

PATH

URL'in path kısmı (/api/v1/partner/...). Host yok, query yok. Trailing slash route'ta varsa korunur.

CANONICAL_QUERY

Query string'in kanonik hali:

  1. ? prefix'i atılır.
  2. Param'lar & ile parçalanır, her biri key=value.
  3. Aynı key birden fazla kez varsa, aynı key içinde value'lara göre de alfabetik sıralanır.
  4. Tüm key'ler ve value'lar RFC 3986 ile encode edilir (%XX, büyük harf hex).
  5. Sonuçlar & ile birleştirilir.
  6. Boş query için bu satır boş string olur.

Örnek:

input:  ?limit=20&cursor=eyJ0…&status=published
output: cursor=eyJ0%E2%80%A6&limit=20&status=published

TIMESTAMP

Header'daki X-Trameks-Timestamp ile tıpa tıp aynı string (entegrasyon farkı olmasın diye).

NONCE

Header'daki X-Trameks-Nonce ile tıpa tıp aynı string.

BODY_SHA256

İstek body byte'larının lowercase hex SHA-256 hash'i.

  • Body yoksa veya boşsa: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  • JSON gönderiyorsanız: tam olarak gönderdiğiniz byte'ları hash'leyin (yeniden serialize etmeyin).

İmza üretimi

signature_hex = hex(HMAC_SHA256(secret_bytes, canonical_string_utf8))
header_value  = "sha256=" + signature_hex

sha256= prefix'i ileride farklı algoritma desteği için ayrılmıştır. sha256= olmadan da kabul edilir ama önerilen formatla gönderin.

Sunucu tarafı doğrulama akışı

  1. 4 header'dan biri eksikse → 401 missing_signature_headers.
  2. |now - timestamp| > 300s401 expired_timestamp.
  3. Key DB'de bulunamadı / REVOKED / EXPIRED / partner SUSPENDED → 401 invalid_key.
  4. (Eğer ipAllowlist boş değilse) kaynak IP listede yoksa → 403 ip_not_allowed.
  5. Kanonik string yeniden hesaplanır, secret AES-GCM ile decrypt edilir, HMAC karşılaştırılır → eşleşmiyorsa 401 invalid_signature.
  6. nonce Redis'te zaten varsa → 401 replay_detected (TTL: 10 dk).
  7. İstek başarılı → handler çalışır, X-RateLimit-* header'ları eklenir.

Güvenlik prensipleri

  • Secret asla ağda gitmez. Sadece imza gider.
  • Timestamp + nonce zorunlu — sızdırılan tek bir istek bile replay edilemez.
  • Sabit zamanlı karşılaştırma — server-side crypto.timingSafeEqual.
  • Saat senkronu — sunucunuzun NTP ile senkronize olduğundan emin olun (5 dk pencere).
  • Secret'ı git'e koymayın — env veya secret manager kullanın.
  • Production ≠ testtmk_live_… üretim datası, tmk_test_… sandbox.

Sonraki: 02 · İmza örnekleri.