Deneyiminizi geliştirmek için çerezler kullanıyoruz. Bu siteyi ziyaret etmeye devam ederek çerez kullanımımızı kabul etmiş olursunuz.Gizlilik Politikası
DKIM'i Uygulamada mı MTA'da mı İmzalamalı: Mimari Kararın Tuzakları
Uygulamanız kusursuz bir DKIM-Signature üretti, Gmail yine de dkim=fail dedi. Sebep neredeyse her zaman aynı: imzayı siz hesapladıktan sonra biri mesajı değiştirdi. İmzayı nereye koyacağınıza mühendislik gerekçesiyle karar verelim.
EvilMail Team2 Ağustos 202612 dk okuma
Uygulamanız kusursuz bir DKIM-Signature başlığı üretti. Anahtar 2048-bit, seçici DNS'te yayında, bh= doğru hesaplandı. Yine de alıcı tarafta Authentication-Results başlığında acı gerçek yazıyor: dkim=fail (body hash did not verify). Sebep neredeyse her zaman aynıdır: imzayı siz hesapladıktan sonra biri mesajı değiştirdi.
DKIM'i nereye koyacağınız — uygulama koduna mı, MTA'ya mı — bir zevk meselesi değil, bir zamanlama meselesidir. Bu yazı "hangisi daha kolay" sorusuna değil, "mesajı telin üzerine çıkmadan önce en son kim değiştiriyor" sorusuna cevap verir. Peşinen net öneri: varsayılan olarak MTA'da, son milter olarak imzalayın. Uygulama katmanına yalnızca somut bir gerekçeniz varsa geçin. Nedenini gösterelim.
Kararı belirleyen tek soru: mesajı en son kim değiştiriyor?
DKIM iki ayrı şey imzalar: seçtiğiniz başlıkların bir hash'i ve gövdenin hash'i (bh= etiketi). Başlık imzası, h=
DKIM Uygulama Katmanında mı MTA'da mı İmzalanmalı? | evilmail.pro — EvilMail Blog
içinde saydığınız başlıkları kriptografik olarak mühürler. Gövde hash'i ise mesaj gövdesinin
tek bir byte'ına
bile bağlıdır. Bir boşluk, bir satır sonu, bir yeniden kodlama —
bh=
çöker ve imza baştan geçersizdir.
Bu kırılganlık bir tasarım hatası değil, bütün mekanizmanın amacı. Ama pratik bir sonucu var: imza her zaman tele en yakın noktada, mesaj üzerindeki tüm dönüşümler bittikten sonra hesaplanmalı. Buna "geç imzala, en son imzala" (sign late, sign last) diyorum; bu yazının omurgası bu tek cümle.
Canonicalization (kanonikleştirme) bu kırılganlığı biraz yumuşatır. c= etiketi başlık ve gövde için ayrı ayrı simple ya da relaxed alır. simple neredeyse hiç toleranslı değildir: bir başlığın katlanması (folding) ya da fazladan bir boşluk imzayı bozar. relaxed ise ardışık boşlukları tek boşluğa indirger, satır sonundaki boşlukları kırpar. Modern kurulumların tamamı c=relaxed/relaxed üretmeli. simple/simple üreten bir sistem, yolda mesaja dokunan her ara sunucuda kırılmaya adaydır.
Aşağıdaki diyagram, imzayı nerede hesapladığınızın sonucunu tek bakışta gösteriyor. Kritik olan, imza noktası ile "tel" arasında kalan bölge — orada mesaja dokunan herhangi bir bileşen imzanızı sessizce öldürür.
MTA'da imzalama: son milter olarak en sağlam yol
Kendi Postfix'inizi işletiyorsanız ve tek ya da birkaç alan adınız varsa, tartışma burada biter: imzayı MTA'ya bırakın. Mesajı artık değiştirmeyeceği anda imzalar, dolayısıyla mutasyon bölgesi sıfırlanır.
İki yaygın seçenek var: OpenDKIM ve rspamd. OpenDKIM klasiktir, tek işi imzalamaktır. Önce anahtarı üretin:
DNS panelinizde bunu tek string'e sıkıştırıp 255 karakter sınırını aşarsanız kayıt sessizce kırılır — canlıda en sık gördüğüm hata bu. OpenDKIM tarafında KeyTable, SigningTable ve TrustedHosts üçlüsünü kurar, sonra Postfix'e milter olarak bağlarsınız:
Kritik nokta milter sırası. Spam filtreniz veya başlık yeniden yazan başka bir milter varsa, bunlar DKIM'den önce gelmeli. smtpd_milters soldan sağa çalışır; DKIM en sonda olmalı ki kendisinden önceki milter'lar mesajı değiştirmeyi bitirsin. Aksi halde tekrar mutasyon bölgesine düşersiniz.
Birden çok alan adı yönetiyorsanız rspamd daha az bakım ister. dkim_signing modülü, From başlığındaki alan adına göre anahtarı otomatik seçer:
use_domain = "header" satırı DMARC hizalaması için hayati: imzayı gönderen zarf adresine göre değil, From: başlığındaki alan adına göre atar. DMARC tam olarak buna bakar.
Yeni kuruyorsanız Ed25519 + RSA çift seçiciyi de değerlendirin (RFC 8463). Ed25519 anahtarı çok daha kısadır (k=ed25519; p=<base64>), ama eski doğrulayıcılar tanımaz. İki seçiciyle imzalarsınız: yeni doğrulayıcılar Ed25519'a, eskiler RSA'ya düşer.
Uygulama katmanında imzalama: ne zaman gerçekten gerekir
Uygulama katmanı varsayılan değil, istisnadır. Ama gerçek gerekçeler var:
Çok kiracılı SaaS. Her müşteri kendi alan adıyla (d=musteri.com) gönderiyor ve her biri için ayrı anahtar tutuyorsunuz. Milter'ı her yeni müşteride yeniden yapılandırmak yerine, imzayı uygulamadan atmak operasyonel olarak daha temiz.
Programatik anahtar rotasyonu. Anahtarları bir API üzerinden döndürmek, seçiciyi çalışma anında değiştirmek istiyorsanız, uygulama katmanı bunu milter yeniden başlatmadan yapar.
Kontrol etmediğiniz bir relay. SES ya da SendGrid kendi imzasını ekler, ama siz kendi d='nizle de imzalamak isteyebilirsiniz. Mesajı relay'e teslim etmeden önce uygulamada imzalarsınız.
Node tarafında nodemailer'ın yerleşik DKIM desteği var:
Python tarafında dkimpy aynı işi görür. Ama burada dev bir tuzak var ve doğrudan bir sonraki bölüme köprü kuruyor: nodemailer mesajı imzalayıp 127.0.0.1:25 üzerinden Postfix'e teslim ederse ve Postfix Content-Transfer-Encoding'i yeniden yazarsa (8bit → quoted-printable), bh= anında çöker. Uygulamada imzalıyorsanız, imzalı mesajı Postfix'in dokunmayacağı bir yoldan göndermek zorundasınız — ya da imzayı en baştan MTA'ya bırakmak.
Sessiz katiller: imzayı bozan dönüşümler
dkim=fail gördüğünüzde suçlu genelde şu altı şeyden biridir:
Content-Transfer-Encoding yeniden yazımı.8bit gövdeyi quoted-printable'a çeviren her ara sunucu gövde byte'larını değiştirir, bh= çöker. En yaygın "temiz imza, fail sonuç" nedeni.
`always_add_missing_headers = yes`. Postfix eksik Date veya Message-ID başlığını ekler. Uygulama bu başlığı zaten yazdıysa çifte başlık oluşur; From gibi bir başlığı h='e koyup mesaja iki kez sokmak da başlık imzasını bozar.
Başlık katlama ve whitespace.simple kanonikleştirmeyle en ufak folding farkı imzayı öldürür. relaxed kullanma sebebiniz bu.
`l=` gövde uzunluk etiketi. Gövdenin yalnızca ilk N byte'ını imzalar. Kulağa esnek gelir ama bir güvenlik açığıdır: saldırgan imzalı gövdenin sonuna içerik ekleyebilir (append attack) ve imza hâlâ geçerli görünür. Asla kullanmayın.
`t=` / `x=` saat kayması. Sunucu saatiniz kaymışsa ya da x= ile kısa bir geçerlilik verdiyseniz, imza "gelecekte" ya da "süresi dolmuş" görünebilir. NTP'nizi kontrol edin.
DMARC hizalaması. İmza teknik olarak geçse bile, d= alan adı From: başlığıyla hizalı değilse DMARC yine fail verir. relaxed hizalamada organizasyon alan adı eşleşmesi yeter (mail.evilmail.pro ↔ evilmail.pro), strict hizalamada birebir aynı olmalı.
Hibrit: çift imza ve ARC
En sofistike kurulum ikisini birden kullanır. Uygulama müşteri alan adıyla imzalar (d=musteri.com), MTA kendi altyapı alan adınızla ikinci bir DKIM-Signature ekler (d=evilmail.pro). Bir mesajın birden çok DKIM imzası taşıması RFC'ce tamamen geçerlidir; doğrulayıcı her birini bağımsız kontrol eder. DMARC için önemli olan, en az bir geçen imzanın From: ile hizalı olması.
Yönlendirme ve mailing-list senaryolarında hikaye değişir. Bir liste sunucusu Subject'e [liste] etiketi ekler ya da altbilgi yapıştırırsa, gövde değiştiği için orijinal DKIM imzası zorunlu olarak kırılır. İşte ARC (Authenticated Received Chain) burada devreye girer: her ara sunucu, mesajı aldığı andaki doğrulama sonucunu kriptografik olarak "mühürler" ve zincire ekler. ARC, DKIM'in yerini almaz; kırılan imzanın olduğu yerde "ben aldığımda bu mesaj geçerliydi" güvencesini taşır. DMARC'ı ARC üzerinden geçirmek alıcının politikasına kalmıştır, ama zinciri hiç sağlamamak forward edilen postanın deliverability'sini öldürür.
Karar ağacı
Doğrulama ve hata ayıklama
İmza atmadan önce DNS'in doğru yayınlandığını görün:
bash
dig +short TXT mail._domainkey.evilmail.pro
opendkim-testkey -d evilmail.pro -s mail -vvv
# "key OK" görmek istiyorsunuz; "key not secure" DNSSEC uyarısıdır, sorun değil
Sonra gerçek bir mesaj gönderin ve alıcı tarafta başlığı okuyun:
dkim=fail (body hash did not verify) görürseniz teşhis nettir: imzadan sonra gövde değişti — mutasyon bölgesine bakın. dkim=fail (signature did not verify) ise başlık tarafında sorun var ya da DNS'teki p= anahtarı özel anahtarınızla eşleşmiyor. Hızlı bir genel sağlık kontrolü için mail-tester.com da işe yarar, ama nihai gerçek her zaman alıcı MX'in Authentication-Results başlığıdır.
Üretime çıkmadan önce kontrol listesi
Anahtar en az 2048-bit RSA; mümkünse Ed25519 ile çift seçici imza kurun.
c=relaxed/relaxed üretin; l= etiketi yok; h= içinde From mutlaka var.
İmza tüm mesaj dönüşümlerinden sonra, tele en yakın noktada atılıyor.
Postfix eksik başlık eklemiyor (always_add_missing_headers durumunu kontrol edin) ya da uygulama Date/Message-ID'yi önceden yazıyor.
d= ↔ From: DMARC hizası doğru (use_domain = "header").
DNS TXT kaydı 255-karakter parçalara doğru bölünmüş; opendkim-testkey "key OK" diyor.
Çok kiracılıysanız: seçici/anahtar haritası ve bir rotasyon planı var.
Yönlendirme/liste senaryosu için ARC değerlendirildi.
swaks + Gmail "Show original" ile prod öncesi dkim=pass bizzat doğrulandı.
İmzayı doğru katmana koymak, deliverability'nin en ucuz ama en çok atlanan kazanımıdır. Kural tek cümleye sığıyor: mesaja en son kim dokunuyorsa, imzayı o atmalı.