Backscatter: як ваш сервер стає спам-гарматою і як відхиляти пошту на RCPT, а не бомбардувати відмовами
Backscatter — це не спам, що прийшов до вас, а спам, який розсилаєте ви: сервер приймає лист на DATA, а потім шле відмову на підроблену адресу. Розбираємо, чому «accept-then-bounce» досі живе в дефолтах, і як у Postfix, rspamd, на backup MX та forwarding приймати рішення синхронно на RCPT TO.
EvilMail Team16 липня 2026 р.12 хв читання
Приходить скарга. Незнайома людина пише, що ваш сервер надіслав їй Undelivered Mail Returned to Sender на лист, якого вона ніколи не писала. Ви відкриваєте mxtoolbox, вбиваєте свій IP — і він світиться в ips.backscatterer.org. Слідом падає доставка на Outlook, бо репутація просіла, а служба підтримки клієнта питає, чому листи йдуть у спам.
Механізм короткий і неприємний. Спамер відкрив з'єднання, у MAIL FROM підставив чужу (або вашу) адресу, надіслав лист на неіснуючий ящик вашого домену. Ваш сервер прийняв усе повністю — відповів 250 на крапку після DATA — а потім, не змігши доставити, чесно згенерував повідомлення про недоставку і надіслав його на ту саму підроблену адресу. Жертва отримала bounce за лист, якого не існувало. Це і є backscatter: спам, який породив ваш сервер.
Це суто архітектурна проблема, і лікується вона однією ідеєю: рішення «так/ні» треба ухвалювати, поки за з'єднання відповідає надсилач — на команді RCPT TO, а не тоді, коли лист уже у вашій черзі.
Два моменти, коли можна сказати «ні»
Backscatter: захист поштового сервера і reject на RCPT замість bounce — EvilMail Blog
SMTP-діалог лінійний: HELO → MAIL FROM → RCPT TO → DATA → крапка → 250. Є рівно дві стратегії поведінки, і різниця між ними — це різниця між чистим IP і потраплянням у блоклисти.
Reject at RCPT. Ваш сервер знає список реальних ящиків ще до DATA. Якщо одержувача не існує, він відповідає 550 5.1.1 <user@domain>: Recipient address rejected прямо на RCPT TO. З'єднання ще відкрите, за нього відповідає надсилач. Легітимний сервер сам згенерує bounce своєму реальному користувачу. Спамер із підробленим MAIL FROM лишається зі своєю проблемою — ви нічого нікому не надсилали.
Accept-then-bounce. Ваш сервер відповідає 250 на всіх етапах, ставить лист у чергу, а вже потім local delivery впирається у «немає такого ящика». Тепер лист ваш. За RFC 5321 §6.1 сервер, що прийняв повідомлення, зобов'язаний або доставити його, або згенерувати DSN. DSN піде на Return-Path, а Return-Path спамер підробив. Ви щойно надіслали спам за власним підписом.
Запам'ятайте одне: `250` на крапку після `DATA` — це точка неповернення. Усе, що прийнято, — уже ваша відповідальність. Backscatter — не порушення RFC, а побічний ефект чесного дотримання RFC на нечесному вхідному потоці. Тому лікування — не «вимкнути bounce», а «не приймати те, що доведеться відбивати».
Чому дефолти досі це роблять
Ніхто не вмикає backscatter свідомо. Він народжується з трьох типових діряв у конфігах.
Довідник підключено пізно. Найчастіший випадок: список ящиків (LDAP/SQL) бачить лише етап local delivery, а smtpd про нього не знає. Postfix каже 250 усім, а потім delivery-агент з'ясовує, що ящика нема. Виправлення — дати smtpd той самий довідник через virtual_mailbox_maps/local_recipient_maps, щоб рішення ухвалювалося на RCPT.
Catch-all. Віртуальний alias @domain → someuser ловить усе підряд, включно з листами на sdfgh@domain від спам-ботів. Далі цей ящик має автовідповідач або forward — і кожен ловлений спам породжує bounce чи автовідповідь на підроблену адресу. Catch-all на бойовому домені 2026 року — це майже завжди помилка.
Backup MX, що приймає все. Вторинний MX приймає пошту, поки первинний лежить, а потім релеїть її назад. Без списку реальних одержувачів він каже 250 навіть на неіснуючі ящики, отримує 550 від первинного при релеї — і робить bounce на (підроблений) Return-Path. Backup MX без синхронного relay_recipient_maps — це backscatter-генератор, що чекає на свою годину.
Postfix: рішення на RCPT
Серцевина. Мета — щоб Postfix знав повний список ящиків і релей-призначень ще до DATA і віддавав 550 синхронно.
ini
# main.cf
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
reject_unknown_recipient_domain,
reject_unverified_recipient
smtpd_reject_unlisted_recipient = yes
unverified_recipient_reject_code = 550
unverified_recipient_reject_reason = Recipient address rejected: undeliverable
# джерело правди про ящики — той самий, що й у delivery
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
local_recipient_maps = $virtual_mailbox_maps $alias_maps
reject_unauth_destination не дає перетворити сервер на open relay. reject_unknown_recipient_domain відсікає домени, яких не існує. smtpd_reject_unlisted_recipient = yes (це дефолт, але задекларуйте явно) змушує Postfix звірятися зі списком ящиків прямо на RCPT — і саме це вбиває accept-then-bounce для неіснуючих адрес.
reject_unverified_recipient — потужний, але з застереженням. Він робить callout-перевірку на бекенд, і якщо ним зловживати, ваш сервер сам стає джерелом верифікаційного трафіку до чужих серверів. Для власних доменів, де ви й так маєте повний virtual_mailbox_maps, він майже не потрібен — ставте його лише для транзитних доменів, де списку ящиків у вас нема.
І принципове правило: якщо лист треба відбити — reject, а не discard і ніколи не accept-then-bounce.discard тихо ковтає пошту (легітимний відправник ніколи не дізнається, що не дійшло), а reject чесно віддає 550 тому, хто ще на дроті.
Backup MX і forwarding — найтихіші джерела
Backup MX мусить мати точно той самий список одержувачів, що й первинний:
Без непорожнього relay_recipient_maps backup MX приймає для будь-якої адреси і бумерангить. Список має синхронізуватися з первинним (rsync по cron або спільна SQL-база) — інакше він розповзеться і дірка повернеться.
Forwarding — окрема пастка. .forward чи virtual alias на зовнішній ящик виглядає невинно, поки цільовий провайдер не відхилить переслане як спам. Тоді 550 прилітає вам, і ви робите bounce на оригінального (часто підробленого) відправника. Плюс переслана пошта провалює SPF на приймачі, бо йде з вашого IP від чужого домену. Рішення — SRS:
SRS переписує MAIL FROM на [email protected], тож переслане проходить SPF, а bounce повертається вам і розкодовується назад коректно. І не форвардьте те, що не пройшло базовий контент-скоринг — не тягніть чужий спам крізь свій IP.
Rspamd/amavis: контент теж відхиляємо на діалозі
Класична помилка — сканувати пошту post-queue: прийняти 250, покласти в чергу, знайти вірус чи спам і... згенерувати нотифікацію відправнику. Відправник підроблений. Знову backscatter, ще й із формулюванням «ваш лист містив вірус».
Правильно — pre-queue milter. Rspamd підключається через proxy worker як milter, і дія reject виконується, поки з'єднання відкрите:
При скорі ≥ 15 rspamd віддає reject → Postfix відповідає 550 синхронно → лист узагалі не потрапляє у чергу, bounce нема кому й нема за що генерувати. greylist = 4 дає soft reject (450), і легітимний сервер повторить.
Для антивірусної логіки в amavis правило 2026 року просте: $final_virus_destiny = D_DISCARD (а краще — детект на pre-queue і D_REJECT), і ніколи не слати «virus notification» назовні. Повідомлення про вірус на підроблену адресу — найтупіший вид backscatter, який існує.
SPF -all, DMARC p=reject і фільтр вхідних DSN
Навіть ідеально налаштований сервер не рятує від чужого backscatter: спамер підставляє ваш домен у From, кривий чужий сервер приймає-й-відбиває, і відмова летить вашому користувачу. Тут працює жорсткий SPF і DMARC:
-all (не ~all) каже приймачам жорстко відкидати пошту не з вашого IP. p=reject разом із коректним DKIM (обов'язково, інакше приб'єте власну легітимну пошту) змушує приймачів відкидати підробки під вашим доменом — а отже й backscatter за ними не породжується.
Останній рубіж — приймати вхідні bounce тільки якщо вони справді ваші. DSN завжди має порожній MAIL FROM:<> — тому bounce на bounce неможливий за визначенням. Але порожній Return-Path мають і чужі backscatter-листи. Відрізнити свій від чужого дозволяє BATV.
BATV/PRVS: підписуємо власний Return-Path
BATV (Bounce Address Tag Validation) зі схемою PRVS вирішує проблему остаточно. Ваш сервер переписує Return-Path на вихідних листах, додаючи криптотег: [email protected], де TAG = HMAC(секрет, дата, адреса). Будь-який чесний bounce на ваш лист прийде на цю тегнуту адресу. Будь-який вхідний DSN, у якому в RCPT немає валідного prvs-тега, — це стовідсотково чужий backscatter, бо ви ніколи не слали з такого Return-Path. Відхиляємо на RCPT з 550.
Exim підтримує BATV вбудовано. У Postfix це робиться milter-ом (наприклад, batv-milter) або policy-сервісом на check_recipient_access. Тег має короткий термін дії (кілька днів), тож старі теги не приймаються, а секрет ротуємо.
Перевірка: чи ви вже в списках
Найшвидший тест — постукати у власний сервер на неіснуючий ящик. Маєте отримати 550, а не 250: