Outlook/Office 365 Geri Bildirim Döngüsü: SNDS ve JMRP'yi Tek Kapalı Sisteme Bağlamak
Microsoft standart RFC FBL'ye katılmaz; elinizdeki tek iki sinyal SNDS ve JMRP'dir. Bu yazı ikisini ayrı panel olarak değil, SNDS'i erken uyarı radarı, JMRP'yi otomatik suppression besleyicisi yapan tek bir kapalı döngü olarak kurar — ve herkesin takıldığı "ARF'ta alıcı adresi kırpık geliyor" tuzağını VERP ile çözer.
EvilMail Team14 Temmuz 202611 dk okuma
Bir kampanya gönderdiniz, ertesi gün Gmail tarafındaki açılma oranınız yerinde ama Outlook.com ve Office 365 alıcılarınızın açılma oranı yere yapıştı. Neden? Bilmiyorsunuz — çünkü Microsoft size Gmail Postmaster Tools'taki gibi hazır bir şikayet akışı vermez. "Önemsiz"e basan Outlook kullanıcısını standart bir RFC FBL üzerinden geri döndürmez. Elinizde sadece iki sinyal var: SNDS (Smart Network Data Services) ve JMRP (Junk Mail Reporting Program). Çoğu ekip bu ikisini iki ayrı sekme gibi ara ara açıp bakar. Yanlış. Bunlar tek bir kapalı döngünün iki ucudur ve ancak birlikte, otomatize edildiğinde işe yarar.
SNDS ve JMRP: aynı madalyonun iki yüzü
İkisi de Microsoft'un IP itibar altyapısına açılan birer pencere, ama doğaları taban tabana zıt.
SNDS IP başına *toplu* telemetridir. Gönderdiğiniz her IP için Microsoft'un kendi filtresinin ne gördüğünü günlük olarak verir: kaç RCPT komutu geldi, kaçı gerçek alıcıya ulaştı, filtre sonucu ne oldu (GREEN/YELLOW/RED), şikayet oranı hangi banda düştü, spam trap'e kaç kez çarptınız. Proaktiftir ama gecikmelidir — veri tipik olarak 1-2 gün geriden gelir. SNDS trendi gösterir: "bu /24 bloğunda dün RED'e geçen üç IP var."
JMRP ise *tekil* ve reaktiftir. Bir Outlook/Hotmail kullanıcısı mailinizi "Önemsiz"e taşıdığında, Microsoft o tek şikayeti ARF (Abuse Reporting Format, RFC 5965) biçiminde tanımladığınız posta kutusuna düşürür. SNDS "oran %0.1-1 bandında" der; JMRP "işte şikayet eden mesajın kendisi" der. Suppression listesini besleyen budur.
Outlook SNDS + JMRP ile Feedback Loop Kurmak | evilmail.pro — EvilMail Blog
Outlook standart FBL'ye katılmadığı için bu ikisinin dışında hiçbir kaynak yoktur. Her ikisi de zorunludur ve her ikisi de aynı IP aralıkları üzerinde tanımlanmalıdır. Microsoft yetkilendirmeyi alan adı değil, IP aralığı üzerinden yapar — bu ayrım, kurulumun her adımını belirler.
Kayıttan önce: PTR, SPF, DKIM ve ayrılmış IP
SNDS/JMRP kaydını açmaya kalkmadan önce IP'nizin hijyeni tamam olmalı, yoksa yetkilendirme adımında duvara toslarsınız.
rDNS (PTR) zorunlu ve gönderen domainle uyumlu olmalı. Generic ISP PTR'ı (212-22-69-210.customer.isp.net gibi) kabul edilmez. Bizde dig -x 203.0.113.10 +short çıktısı mail.example.net döner — MAIL FROM domaini ile aynı yönetim alanında. PTR yoksa Microsoft yetki kodunu göndereceği admin adresini bile çözemez.
SPF ve hizalı DKIM. SPF gönderen IP'yi yetkilendirmeli, DKIM imzası d= domaininizle hizalı olmalı. Bunlar SNDS'in ön koşulu değil ama RED filtre sonucunun en yaygın sebebidir; eksikken SNDS verisi baştan kirli gelir.
Ayrılmış/statik IP. Paylaşımlı IP'de SNDS ve JMRP verisi anlamsızdır — komşularınızın davranışı sizinkiyle karışır, RCPT sayıları ve şikayet bandı sizin değildir.
IP aralığı mantığı. Kayıt IP bazlı olduğu için tek /32 yerine kullandığınız tüm bloğu (/29, /24) girin. Yarın gönderime ekleyeceğiniz IP zaten yetkili olsun; her yeni IP için baştan yetkilendirme beklemekten kurtulursunuz.
SNDS kaydı ve IP yetkilendirme akışı
https://postmaster.live.com/snds/ adresine bir Microsoft (MSA) hesabıyla girin. "Register IP addresses" ile aralığı ekleyin — örneğin 212.22.69.208/29. Microsoft bu noktada bir yetki kodu üretir ve onu IP'nin rDNS domaininden ya da WHOIS kaydından türettiği şu adreslerden birine yollar: admin@, postmaster@, abuse@, hostmaster@, webmaster@. Doğru kutuyu seçip gelen kodu SNDS ekranına girdiğinizde aralık yetkili hale gelir.
Kurulumun en kırılgan noktası burası: yetki maili gidecek kutu gerçekten açık ve okunabilir olmalı. abuse@ adresini hiç oluşturmadıysanız kod boşluğa düşer. Onaydan sonra "Data" ekranının dolması 24-48 saat sürer — ilk gün boş görürseniz panik yapmayın.
SNDS verisini otomatik çekmek
Web arayüzüne el ile bakmak ölçeklenmez. SNDS'in "Automated Data Access" bölümünden bir GUID key üretin; bu anahtar iki CSV endpoint'ini kimlik doğrulamasız çekmenizi sağlar. Cron'a bağlayın:
bash
#!/usr/bin/env bash
# /opt/snds/pull.sh — her sabah 07:00'de çalışır
set -euo pipefail
SNDS_KEY="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
OUT=/var/log/snds
mkdir -p "$OUT"
DAY=$(date +%F)
# IP başına günlük aktivite (RCPT, filtre sonucu, şikayet bandı, trap)
curl -fsS "https://postmaster.live.com/snds/data.aspx?key=$SNDS_KEY" \
-o "$OUT/data-$DAY.csv"
# Blok durumu ve sebebi
curl -fsS "https://postmaster.live.com/snds/ipStatus.aspx?key=$SNDS_KEY" \
-o "$OUT/ipstatus-$DAY.csv"
data.aspx CSV kolonları sırayla: IP, Activity Period Start, Activity Period End, RCPT Commands, DATA Commands, Message Recipients, Filter Result, Complaint Rate, Trap Message Period Start, Trap Message Period End, Trap Hits, Sample HELO, Sample MAIL FROM.
İki değeri karıştırmayın: Filter Result Microsoft'un mesajı nereye koyduğudur (GREEN = inbox, YELLOW = kısmen önemsiz, RED = çoğunlukla önemsiz). Complaint Rate ise kullanıcıların ne kadar şikayet ettiğidir ve bant olarak gelir (< 0.1%, 0.1-1% gibi). Bir gün RED çıkarken Complaint Rate düşük olabilir — o zaman sorun kullanıcı şikayeti değil, içerik/itibar tarafındadır. Trap Hits sıfırdan büyükse liste hijyeniniz bozuk demektir; satın alınmış veya eskimiş adresler var.
JMRP: geri bildirim döngüsünü açmak
Aynı IP aralıklarını https://sendersupport.olc.protection.outlook.com/snds/JMRP.aspx üzerinden kaydedin ve şikayetlerin düşeceği FBL kutusunu tanımlayın — bizde [email protected]. Bir Outlook kullanıcısı "Önemsiz"e bastığında bu kutuya ARF mesajı gelir.
Herkesin çıkmaza girdiği yer tam burası: Outlook'un ARF raporunda orijinal alıcı adresi çoğu zaman kırpık veya güvenilmezdir. Gömülü message/rfc822 parçasındaki To: başlığına bakıp "kim şikayet etti"yi çıkaramazsınız. Microsoft gizlilik gerekçesiyle bu bilgiyi temizler ya da eksik bırakır.
Çözüm gönderim tarafında yatar: her mesaja alıcıyı geri çözebileceğiniz bir işaret gömün.
VERP return-path:[email protected] — alıcının base64'ü return-path'e kodlanır. ARF'ta return-path korunduğu için buradan aboneyi çözersiniz.
veya özel header: her gönderide X-EvilMail-Recipient: ya da standart Feedback-ID: başlığı. Gömülü rfc822 parçasında header'lar genelde sağ kalır.
Bu adım opsiyonel değil. VERP veya özel header olmadan JMRP kutunuz şikayetlerin sayısını verir ama *kimin* şikayet ettiğini asla vermez, dolayısıyla suppression'ı besleyemezsiniz.
Şikayetleri işlemek: ARF ayrıştırma ve suppression
FBL kutusunu IMAP'ten okuyup ARF'ı ayrıştıran ve aboneyi suppression'a yazan minimal Python. ARF üç parçalıdır: Content-Type: multipart/report; report-type=feedback-report altında (1) insan-okur açıklama, (2) message/feedback-report, (3) message/rfc822 orijinal mesaj.
python
import email, base64, re
from email import policy
def parse_arf(raw: bytes):
msg = email.message_from_bytes(raw, policy=policy.default)
# 2. parça: yapısal feedback-report
fb = next((p for p in msg.walk()
if p.get_content_type() == "message/feedback-report"), None)
if fb is None:
return None # ARF değil, atla
fields = dict(re.findall(r"^([\w-]+):\s*(.+)$",
fb.get_payload(decode=True).decode("utf-8", "replace"),
re.MULTILINE))
if fields.get("Feedback-Type", "").lower() != "abuse":
return None
# 3. parça: gömülü orijinal mesaj — alıcıyı buradan çöz
orig = next((p for p in msg.walk()
if p.get_content_type() == "message/rfc822"), None)
inner = orig.get_payload(0) if orig else None
recipient = None
if inner:
# Önce Feedback-ID, sonra VERP return-path
fid = inner.get("Feedback-ID") or inner.get("X-EvilMail-Recipient")
if fid:
recipient = fid.split(":")[-1].strip()
else:
rp = inner.get("Return-Path", "")
m = re.search(r"bounce\+([A-Za-z0-9+/=]+)@", rp)
if m:
recipient = base64.b64decode(m.group(1)).decode("utf-8", "replace")
return {"recipient": recipient, "source_ip": fields.get("Source-IP")}
Çözülen adresi suppression tablosuna idempotent yazın — aynı abone birden çok kez şikayet edebilir, iki kez yazmak hata olmamalı:
sql
INSERT INTO suppression (email, reason, source_ip, created_at)
VALUES ($1, 'outlook_jmrp', $2, now())
ON CONFLICT (email) DO NOTHING;
İşlenen maili Processed klasörüne taşıyın ya da silin ki döngü aynı şikayeti tekrar okumasın. Kritik nokta: bu suppression tablosu gönderim pipeline'ında send-öncesi kontrol edilmeli. Şikayet eden aboneye bir sonraki kampanyada tek bir mesaj daha giderse tüm döngü boşa gider — JMRP'nin varlık sebebi o adrese bir daha dokunmamanızdır.
Eşikler, izleme ve alarm
Hedef şikayet oranı < %0.3. Bu, Microsoft'un junking'e yeşil ışık yaktığı çizginin altıdır. SNDS Complaint Rate bandı 0.1-1%'e sıçradığı gün not alın.
RED filtre günlerini kampanya takviminizle korele edin.data-*.csv dosyalarında bir IP'nin RED'e geçtiği tarihi, o gün hangi segmente ne gönderdiğinizle eşleştirin. Suçlu kampanya neredeyse her zaman tek bir listedir.
Trap Hits = 0 hedefi. Sıfırdan sapma anında liste hijyeni araştırması başlatır — o dönemde eklenen kaynak nedir?
ipStatus.aspx'te blok görürseniz sebebi okuyun; genelde ani hacim artışı veya trap isabetidir. Gönderimi kısıp Microsoft'un delisting/mitigation formunu doldurun.
Eşik aşımında alarm. SNDS ~1-2 gün gecikmeli olduğu için gerçek zamanlı frenleme görevi JMRP'nindir; SNDS ise trend alarmı içindir. CSV'yi parse edip Complaint Rate bandı yükseldiğinde Slack webhook'una bir satır atın: