İletişim

HMAC (İmza Doğrulama) Nedir?

Kısa tanım

HMAC (Hash-based Message Authentication Code), bir mesajı ve yalnızca gönderen ile alıcının bildiği gizli bir anahtarı SHA-256 gibi bir hash fonksiyonundan geçirerek kısa bir doğrulama kodu üreten yöntemdir. Alıcı aynı hesaplamayı yapıp sonucu karşılaştırır; eşleşme, mesajın yolda değişmediğini ve anahtara sahip biri tarafından üretildiğini gösterir. Webhook imzaları, API istek imzalama ve JWT'nin HS256 algoritması HMAC kullanır.

Diğer adları: Hash-based Message Authentication Code, HMAC-SHA256, mesaj doğrulama kodu, HMAC imzası

İleti ile paylaşılan gizli anahtardan HMAC imzası üretildiğini ve alıcının imzayı yeniden hesaplayıp karşılaştırarak doğruladığını gösteren akış

Neden düz bir hash yetmez?

Bir mesajın hash değerini mesajla birlikte göndermek bütünlüğü korumaz: mesajı değiştiren biri hash'i de yeniden hesaplayıp gönderebilir, çünkü SHA-256 herkesin kullanabildiği bir fonksiyondur. Doğrulamanın anlamlı olması için hesaplamaya saldırganın bilmediği bir şeyin, yani gizli bir anahtarın girmesi gerekir.

Akla gelen ilk çözüm, anahtarı mesajın başına ekleyip hash almaktır: SHA256(anahtar + mesaj). Bu yaklaşım SHA-256 gibi Merkle–Damgård yapısındaki fonksiyonlarda “uzunluk uzatma” (length extension) zayıflığı taşır: anahtarı bilmeyen biri, geçerli bir imzadan yola çıkarak mesajın sonuna veri eklenmiş bir sürüm için de geçerli bir imza üretebilir. HMAC bu sorunu iç içe iki hash ile çözer. RFC 2104'teki tanım kabaca şöyledir:

HMAC(K, m) = H( (K ⊕ opad) ‖ H( (K ⊕ ipad) ‖ m ) )

ipad = 0x36 baytının tekrarı, opad = 0x5C baytının tekrarı

Pratikte bu formülü kimse elle yazmaz; her dilin standart kütüphanesinde hazır bir HMAC fonksiyonu vardır ve kendi uygulamanızı yazmak yerine onu kullanmalısınız.

HMAC neyi garanti eder, neyi etmez?

YöntemBütünlükKaynak doğrulamaGizlilikİnkâr edilemezlik
Düz hashYalnızca kazara bozulmaya karşıHayırHayırHayır
HMACEvetEvet, anahtarı paylaşan taraflar arasındaHayırHayır
Dijital imza (RSA, ECDSA)EvetEvetHayırEvet
ŞifrelemeYönteme bağlıYönteme bağlıEvetHayır

İki nokta sık karıştırılır. Birincisi, HMAC mesajı gizlemez; webhook gövdesi açıkça okunabilir, gizlilik TLS ile sağlanır. İkincisi, anahtar iki tarafta da bulunduğu için alıcı da geçerli bir imza üretebilir. Bu yüzden HMAC, “bu mesajı kesinlikle gönderen taraf üretti” iddiasını üçüncü bir kişiye kanıtlamaya yetmez; bunun için açık anahtarlı dijital imza gerekir.

Webhook imzasını doğrulamak

Ödeme, kargo ve kod barındırma servislerinin çoğu webhook isteklerini HMAC-SHA256 ile imzalar. Ayrıntılar servise göre değişse de doğru doğrulamanın adımları aynıdır:

  1. Ham gövdeyi alın. İmza, ağdan gelen baytlar üzerinden hesaplanmıştır. Gövdeyi JSON olarak ayrıştırıp yeniden serileştirirseniz anahtar sırası ve boşluklar değişir, imza tutmaz. Web framework'ünün gövdeyi otomatik ayrıştırmasını bu uç nokta için kapatmanız gerekebilir.
  2. İmzalanan metni servisin belgesine göre kurun. Bazı servisler yalnızca gövdeyi, bazıları zaman damgası ile gövdenin birleşimini (ör. zamanDamgasi + "." + gövde) imzalar.
  3. Beklenen imzayı hesaplayın ve sabit zamanlı karşılaştırın.
  4. Zaman damgasını kontrol edin. Birkaç dakikadan eski istekleri reddetmek, yakalanmış geçerli bir isteğin sonradan tekrar gönderilmesini (replay) sınırlar. Olay kimliğini kaydedip tekrar gelenleri işlememek de aynı amaca hizmet eder.
import { createHmac, timingSafeEqual } from "node:crypto";

// hamGovde: ayrıştırılmamış istek gövdesi (Buffer)
function imzaGecerliMi(hamGovde, gelenImza, gizliAnahtar) {
  const beklenen = "sha256=" + createHmac("sha256", gizliAnahtar)
    .update(hamGovde)
    .digest("hex");
  const a = Buffer.from(beklenen);
  const b = Buffer.from(gelenImza ?? "");
  // timingSafeEqual farklı uzunluklarda hata fırlatır
  return a.length === b.length && timingSafeEqual(a, b);
}

Örnekteki sha256= öneki GitHub'ın biçimidir; başka servislerde önek, kodlama (hex veya Base64) ve başlık adı farklı olabilir.

Sabit zamanlı karşılaştırma neden önemli?

Sıradan bir metin karşılaştırması (===) ilk farklı karakterde durur. İlk karakteri yanlış bir imza, ilk on karakteri doğru olan bir imzadan çok az da olsa daha hızlı reddedilir. Bu fark tek istekte ölçülemeyecek kadar küçüktür, ama çok sayıda istekte istatistiksel olarak ortaya çıkabilir ve teorik olarak imzanın bayt bayt tahmin edilmesine kapı aralar. Node.js'teki crypto.timingSafeEqual gibi fonksiyonlar, eşleşme nerede bozulursa bozulsun aynı sürede çalışır. Diğer dillerde de karşılığı vardır: Python'da hmac.compare_digest, PHP'de hash_equals.

Örnekteki uzunluk kontrolü ayrıca gereklidir, çünkü Node.js'in fonksiyonu farklı uzunluktaki girdilerde hata fırlatır. Bu kontrolün sızdırdığı tek bilgi imzanın uzunluğudur ve o zaten herkesçe bilinir.

Anahtar seçimi, saklama ve rotasyon

  • Uzunluk ve rastgelelik: RFC 2104, hash çıktısından (SHA-256 için 32 bayt) kısa anahtarların kullanılmamasını açıkça tavsiye eder. Anahtar, kriptografik olarak güvenli bir rastgele üreteçle oluşturulmalıdır; insan tarafından seçilen bir parola anahtar değildir.
  • Saklama: Anahtar kod deposuna değil, ortam değişkenine veya bir gizli bilgi yönetimi servisine konur.
  • Ayrı anahtarlar: Her entegrasyon ve her ortam (test, canlı) için ayrı anahtar kullanmak, bir sızıntının etkisini sınırlar.
  • Rotasyon: Anahtar değiştirilirken kısa bir süre hem eski hem yeni anahtarla üretilen imzaları kabul etmek, kesintisiz geçişi sağlar.

HMAC yalnızca webhook'larda karşınıza çıkmaz. JWT'lerin HS256 algoritması bir HMAC-SHA256'dır; bulut sağlayıcılarının API istek imzalama şemaları ve kimlik doğrulama uygulamalarının ürettiği tek kullanımlık kodlar (HOTP/TOTP) da HMAC üzerine kuruludur.

İlgili terimler

← Sözlüğe dön