Безопасность Postfix. Часть 3

Пассивная безопасность заключается в предоставлении принимающему серверу дополнительной информации об отправителе, благодаря которой он может выбрать оптимальный способ обработки сообщения на основе внутренних политик или предпочтений пользователя. Это может быть указание контактных данных в полях *-c-email записей WHOIS домена, дополнительные заголовки в письме или использование SPF / DKIM / DMARC (их, в отличии от TLS, поддерживают все крупные почтовые сервисы)

Использование SPF

Использование технологии SPF позволяет указать принимающему серверу список адресов хостов, которые имеют право отправлять почту от имени домена. Для использования SPF достаточно на DNS, обслуживающем домен отправителя, завести TXT запись вида:

example.com. IN TXT "v=spf1 a mx -all"

Более подробно с полями записи SPF можно ознакомиться на сайте openspf.org. В данном примере мы разрешили принимать почту со всех серверов, на которые указывают A и MX записи домена и запретили для всех остальных (-all)

При использовании SPF следует обратить внимание на следующие моменты:

  • Использование SPF является лишь рекомендацией для принимающей стороны — далеко не все почтовые сервисы отвергнут письмо, пришедшее с «чужого» IP адреса только на основании того, что в записи установлено значение -all
  • Если MX записи домена указывают на IPv6 адреса, то при проверке они не разворачиваются механизмом mx и эти адреса требуется перечислить в явном виде через механизм ip6 (точно так же, механизм a задает только IPv4 адреса домена, но не IPv6)
  • Если длина SPF записи превышает 1000 байт (приблизительно), рекомендуется разбить запись на части с использованием механизма include

Использование DKIM

Технология DKIM позволяет подтвердить отправителя сообщения и неизменность содержимого сообщения. Для автоматической подписи всех исходящих сообщений на сервере должен быть установлен dkim-filter. Конфигурация фильтра задается в файле /etc/dkim-filter.conf в виде:

Syslog yes
Selector default
Mode s
InternalHosts /etc/mail/dkim-hosts.conf
KeyList /etc/mail/dkim-keys.conf
Socket inet:10034@127.0.0.1

Здесь:

  • Syslog — вести лог работы фильтра
  • Selector — селектор по умолчанию при использовании KeyList
  • Mode — режим работы только на подпись сообщения (без проверки подписи)
  • InternalHosts — список хостов, для которых должны подписываться сообщения (вместо проверки)
  • KeyList — имя файла с конфигурацией ключей для подписи сообщений (см. ниже)
  • Socket — адрес и порт, на котором фильтр будет ожидать входящие соединения (существует возможность задать имя файл-сокета, но при работе Postfix в chroot это потребует дополнительных настроек)

Файл dkim-hosts.conf представляет собой список имен хостов или IP адресов (по одному на каждую строку) исходящая почта с которых будет подписываться фильтром. В простейшем случае:

localhost
example.com

Файл dkim-keys.conf представляет собой набор строк с указанием (через двоеточие) маски отправителя, имени подписывающего домена и имени файла ключа для подписи. Например:

*@example.com:example.com:/etc/mail/key/default

Здесь письма с отправителем из домена @example.com будут подписываться подписью домена example.com с селектором default (был задан ранее в конфигурации), ключ которого задан в файле default

Для создания приватного и публичного ключей DKIM необходимо, находясь в директории /etc/mail/key, воспользоваться утилитой dkim-genkey. Запуск утилиты без параметров создаст пару файлов: default.private и default.txt, которые являются приватным ключом и текстом TXT записи, которую требуется добавить в DNS. Файл default.private необходимо переименовать в default

В файле default.txt приведено содержимое TXT записи для DNS. Дополнительно, рекомендуется создать следующие записи:

default._domainkey.example.com. IN TXT "v=DKIM1; g=*; k=rsa; ..."
_adsp._domainkey.example.com. IN TXT "dkim=discardable"
_domainkey.example.com. IN TXT "o=-; r=admin@example.com"

Здесь:

  • default._domainkey — публичный DKIM ключ для селектора default домена example.com (подробности в RFC 4870)
  • _adsp._domainkey — подпись должна быть сделана тем же доменом, который указан в адресе отправителя, в противном случае получатель может отбросить письмо (второе допустимое значение all, которое является более мягким, подробности в RFC 5617)
  • _domainkey — политика подписи (o=- — подписываются все сообщения, r= — адрес для отчетов о невалидной подписи)

Для включения подписи исходящих сообщений в Postfix необходимо добавить конфигурацию:

smtpd_milters = inet:127.0.0.1:10034
non_smtpd_milters = inet:127.0.0.1:10034

Здесь smtpd_milters и non_smtpd_milters задают адрес и порт фильтра dkim-filter для сообщений проходящих через сервер smtpd и локальных сообщений, отправленных через утилиту sendmail

При использовании фильтра DKIM следует обратить внимание на формат перевода строк в теле письма — фильтр требует перевода строк в стиле unix (\n) в то время как SMTP требует окончание строк CRLF — необходимые преобразования производятся автоматически

Использование DMARC

Использование технологии DMARC позволяет явно указать получателю письма рекомендуемые действия в случае, если письмо не проходит проверку SPF и/или DKIM
и получать отчеты о применении технологии почтовым сервисом. Так же, как и предыдущие техники, политика конфигурируется через TXT запись на DNS домена или поддомена в виде:

example.com. IN TXT "v=DMARC1; aspf=s; adkim=s;
p=reject; sp=reject; rua=mailto:admin@example.com"

В данном примере мы включаем строгую проверку SPF и DKIM записи для домена (подпись должна быть сделана тем же доменом, с которого отправлено сообщение) и рекомендуем отклонять все сообщения для домена и всех поддоменов в случае ошибки проверки с отсылкой отчетов на адрес admin@example.com (к сожалению, отсылка отчетов не работает ни у кого — возможно в будущем)

С примерами записей и назначением полей можно ознакомиться в справочных системах mail.ru, google.com или на сайте инициаторов инициативы dmarc.org

Использование дополнительных заголовков

Сообщения, генерируемые автоматически, так или иначе попадают под определение «массовые рассылки». Для массовых рассылок у крупных почтовых сервисов устанавливаются свои «правила хорошего тона» соблюдая которые можно улучшить процент доставки и прочтения сообщений получателями. С общими требованиями можно ознакомиться в документе Senders Best Communications Practices (PDF). С требованиями основных почтовых сервисов можно ознакомиться по ссылкам:

Из технических требований к массовым рассылкам можно выделить следующие пункты:

  • Ящик отправителя должен существовать (об этом говорилось ранее)
  • Должны использоваться SPF / DKIM / DMARC (о которых говорилось ранее)
  • Рассылку рекомендуется вести с выделенного домена (например, robot.example.com вместо example.com) — это позволит разделить личную и автоматическую корреспонденцию как для удобства получателей так и для собственного (личная корреспонденция на основном домене может обслуживаться сторонним сервисом, например Google Apps)
  • Сервер должен иметь валидную PTR запись (запись, которая позволяет получить имя хоста по IP адресу). Обычно обслуживанием зоны in-addr.arpa занимается техподдержка хостера, однако, современные хостеры (типа Hetzner) позволяют управлять PTR записями из панели управления сервером. Для проверки PTR записи сервера можно воспользоваться утилитой nslookup указав в качестве аргумента требуемый IP адрес
  • Каждое сообщение должно снабжаться заголовками Precedence: bulk и List-Unsubscribe. Подробнее с форматом заголовка List-Unsubscribe можно ознакомиться на сайте list-unsubscribe.com — в общем случае его значением
    является URL, который позволяет отписать пользователя без совершения дополнительных действий (типа авторизации на сайте) и/или почтовый адрес для отсылки уведомления о желании пользователя отписаться от рассылки. Параметрами URL могут являться идентификатор пользователя и некоторый хэш, проверка которого позволяет совершить требуемое действие
  • Принадлежность к массовым автоматическим рассылкам указывается заголовками Precedence: bulk и Auto-Submitted: auto-generated

Дополнительно, имеет смысл зарегистрировать почтовый домен в сервисе postmaster.mail.ru для отслеживания ошибок конфигурации и FBL. При использовании данного сервиса может оказаться полезным использование заголовка X-Mailru-Msgtype

Проверка валидности e-mail

При регистрации на сайте и осуществлении подписки бывает важно проверить валидность указанного e-mail адреса как на соответствие стандартам, так и на возможные опечатки (например, mail.ry вместо mail.ru) или наличие домена в списке блокируемых (например, mailinator.com)

В случае отсутствия проверки на этапе регистрации, отсылку почты на домены с опечатками и домены без авторизации можно блокировать через карту виртуальных адресов virtual_alias_maps как это было описано ранее для bounce сообщений

Почтовый адрес

RFC 2822 гласит:

  • Почтовый адрес состоит из имени и домена, разделенных символом «@» (раздел 3.4.1)
  • Имя может состоять из букв, цифр и символов: «!», «#», «$», «%», «&», «’», «*», «+», «-», «/», «=», «?», «^», «_», «‘», «{», «|», «}», «~»: (раздел 2.3.4), а также символа «.» (точка) за исключением того, что имя не может начинаться с точки, заканчиваться на точку или содержать две точки подряд
  • Имя может быть заключено в двойные кавычки «»» и содержать уже любые символы (раздел 3.2.5)
  • Максимальная длина имени составляет 64 символа
  • Имя регистро-зависимо за исключением служебного имени postmaster (RFC 5321, раздел 4.1.1.3)
  • Для совместимости с устаревшим RFC-822, адрес может быть заключен в треугольные скобки и содержать символ «@» (раздел 4.4)
Имя домена

RFC 1035 гласит:

  • Общая длина имени домена ограничена 255 символами, длина имени узла ограничена 63 символами (раздел 2.3.4)
  • Имя узла может состоять из букв, цифр и символа «-» за исключением начала и окончания
  • Узлы разделяются символом «.» (точка)
  • Имя домена регистро-независимо
  • Имя домена может заканчиваться на символ «.» (точка), подразумевая корневой узел, хотя обычно последняя точка не пишется (подробнее см. FQDN)
У кого длиннее?
  • В качестве примера использования потенциала длины имени можно привести проект, где предлагается бесплатно получить самый длинный email адрес и почувствовать на себе все прелести обработки такого адреса веб-формами, почтовым программным обеспечением, системами антиспама и т.д.
  • Максимальная длина почтового адреса, согласно RFC 5321 (раздел 4.5.3.1.3) составляет 256 символов (при этом остается ограничение на длину имени в 64 символа и длину домена в 255 символов)
Примеры

RFC 3696 дает нам несколько занимательных примеров валидных почтовых адресов:

<@hosta.int,@jkl.org:userc@d.bar.org>
jsmith@[192.168.2.1]
Abc\@def@example.com
Abc\\@example.com
Abc\\\@def@example.com
Fred\ Bloggs@example.com
Joe.\\Blow@example.com
"Abc@def"@example.com
"Fred Bloggs"@example.com
user+mailbox@example.com
customer/department=shipping@example.com
$A12345@example.com
!def!xyz%abc@example.com
_somename@example.com

Несмотря на валидность адресов в примере выше, большинство почтовых систем имеют свои ограничения на имя пользователя и пропуск «экзотических» форм адресов в качестве валидных может приносить больше вреда, чем пользы, усложняя логику проверок и внося дополнительные ошибки

Что делать?
  • Регулярное выражение из Perl модуля Mail::RFC822::Address для проверки адреса по RFC 822
  • Email::Valid — модуль Perl, поддерживающий проверку по RFC 822 и дополнительные проверки (существование MX и т.д.)
  • Email::Address — еще один модуль Perl, но уже для проверки по RFC 2822
  • Zend_Validate_EmailAddress — класс из ZendFramework
  • Облегченный парсер для PHP (без проверки MX и т.д.)

Анализ bounce сообщений

Вместо удаления bounce сообщений в /dev/null, как это было предложено ранее, может быть полезным их автоматический анализ с целью блокировки несуществующих адресов или предотвращения повторной отправки на несуществующий адрес для улучшения репутации в глазах крупных почтовых систем

По историческим причинам bounce сообщения формируются в формате, удобном для анализа человеком, но не машиной. Несмотря на существование стандарта RFC 3464, который призван решить эту проблему, современное состояние дел далеко от идеала и разные почтовые сервера продолжают использовать собственные форматы

Для получения возможности анализировать тело сообщения, вернувшегося на адрес отправки (например, noreply@example.com), можно воспользоваться все той же таблицей virtual_alias_maps указав в качестве приемника не адрес получателя, а скрипт обработки сообщения:

noreply: "|/etc/postfix/scripts/noreply.sh"

Скрипт обработки (в примере noreply.sh) должен принадлежать пользователю mail (chown mail:mail noreply.sh) и иметь бит исполнения (chmod 0555 noreply.sh). Содержимое скрипта можно представить в виде:

#!/bin/bash

PID_TEMP_FILE="/tmp/noreply@example.com.$$.tmp"

cat > "${PID_TEMP_FILE}"
TEMP_FILE=$(egrep -i ’^Message-Id:’ "${PID_TEMP_FILE}" | \
 /usr/bin/head -n 1 | /usr/bin/awk ’{print $2;}’ | \
 /usr/bin/cut -d ’<’ -f 2 | /usr/bin/cut -d ’>’ -f 1)

TEMP_FILE="/tmp/bounce/${TEMP_FILE}"

if [ ! -d "/tmp/bounce" ]; then
   mkdir "/tmp/bounce"
   if [ $? -ne 0 ]; then
      rm -f "${PID_TEMP_FILE}"
      exit 0
    fi
fi

mv -f "${PID_TEMP_FILE}" "${TEMP_FILE}"

if [ $? -ne 0 ]; then
   rm -f "${PID_TEMP_FILE}"
   exit 0
fi

if [ -f "/etc/postfix/scripts/analyze.sh" ]; then
   /etc/postfix/scripts/analyze.sh "${TEMP_FILE}" 2>&1 > /dev/null
fi

Приведенный в примере скрипт совершает следующую последовательность действий:

  • Сохраняет тело сообщения во временный файл (для получения дополнительной
    информации)
  • Получает из временного файла значение поля Message-Id (см. далее)
  • Проверяет наличие директории для хранения файлов /tmp/bounce и создает ее в случае необходимости
  • Перемещает временный файл в файл с именем значения поля Message-Id (для простоты дальнейшей диагностики и сохранения уникальности имени файла)
  • Запускает скрипт анализа содержимого analyze.sh, передавая в качестве параметра имя файла с телом сообщения и подавляя любой вывод ошибок
  • При отсутствии скрипта analyze.sh содержимое сообщения просто будет сохраняться в директории /tmp/bounce в файле с уникальным именем и пригодно для дальнейшего анализа

Сам скрипт анализа содержимого сообщения может быть достаточно нетривиальным и качество его работы зависит исключительно от мастерства автора скрипта (дополнительно можно попробовать использовать готовые анализаторы типа Mail::DeliveryStatus::BounceParser). В качестве ориентиров можно посоветовать использовать поиск значений заголовков:

  • X-AutoReply — автоответчик mail.ru (не является bounce сообщением и должно игнорироваться)
  • X-Autogenerated — автоответчик rambler.ru (так же не является bounce сообщением)
  • Diagnostic-Code — собственно, описание причины возврата

В результате работы анализатора должно приниматься решение о (не)возможности или (не)целесообразности дальнейшей отправки сообщений данному адресату. При этом, следует проявлять осторожность, т.к. формально злоумышленнику ничто не мешает сгенерировать нужное количество fake-bounce сообщений и заблокировать всю базу пользователей

Наиболее удобным способом хранения подобной информации является ее хранение в базе данных общего назначения (MySQL, PostgreSQL). Для обеспечения возможности Postfix делать запросы в базу данных требуется установка дополнительного пакета (например, postfix-pgsql для Debian) или сборка Postfix с поддержкой соответствующих возможностей (для FreeBSD)

Для блокирования отправки сообщений получателям из базы данных требуется подключить дополнительную карту виртуальных адресов:

virtual_alias_maps =
 hash:/etc/postfix/virtual,
 proxy:pgsql:/etc/postfix/virtual.cf

Сам файл virtual.cf представляет собой файл конфигурации с параметрами соединения с базой данных и целевым SQL запросом (список поддерживаемых баз и параметры конфигурации описаны в DATABASE_README):

hosts = psql.example.com
user = bouncemail
password = ******
dbname = bouncemail
query = SELECT ’devnull’ FROM bouncemail WHERE email = ’%s’

В приведенном примере SQL запроса все сообщения на почтовые адреса, которые присутствуют в таблице bouncemail базы, будут отправлены в алиас devnull (который, в свою очередь, развернется в /dev/null — «шредер»). Управление списком адресов в базе или критериями выборки остается на усмотрение владельца почтового сервера

Anton Batenev

Часть1, Часть 2

Статьи по теме