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

Предисловие

Однажды, настраивая сервер, я совершенно случайно обнаружил, что отправляемая с сервера почта идет по открытому каналу связи несмотря на то, что настройки почтового клиента позволяли начинать защищенный сеанс в случае поддержки данного функционала принимающим почтовым сервером. В процессе решения данной проблемы выяснилась занимательная деталь — все исходящие соединения на 25-й порт проксировались через сторонний сервис, который в процессе работы изменял проходящие через него данные, нарушая тем самым предполагаемый ход сеанса связи. Выглядело это приблизительно так:

$ telnet gmail-smtp-in.l.google.com 25
Trying 173.194.70.27...
Connected to gmail-smtp-in.l.google.com.
Escape character is ’^]’.
220 ************************************************
EHLO example.com
250-mx.google.com at your service, [xxx.xxx.xxx.xxx]
250-SIZE 35882577
250-8BITMIME
250-XXXXXXXA
250 ENHANCEDSTATUSCODES

Здесь особого внимания заслуживают «странные» строки:

220 ************************************************
...
250-XXXXXXXA

Для сравнения, посмотрим на начало «правильного» сеанса связи, полученного с другого сервера:

$ telnet gmail-smtp-in.l.google.com 25
Trying 173.194.71.27...
Connected to gmail-smtp-in.l.google.com.
Escape character is ’^]’.
220 mx.google.com ESMTP xxxxxxxxxxxxxxxxxxxx - gsmtp
EHLO example.com
250-mx.google.com at your service, [xxx.xxx.xxx.xxx]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250 ENHANCEDSTATUSCODES

Сравнивая оба сеанса мы видим различие двух строк, которые были «испорчены» сторонним сервисом:

220 mx.google.com ESMTP xxxxxxxxxxxxxxxxxxxx - gsmtp
...
250-STARTTLS

Порча первой строки не представляет никакой опасности — SMTP клиенту важен только код 220, а приглашение (или т.н. «баннер») может быть любым. Любопытна вторая измененная строка — 250-STARTTLS, в которой анонсируется возможность удаленного SMTP сервера начать защищенный сеанс связи. Из за вмешательства стороннего сервиса, который скрыл информацию о наличии данного расширения на сервере, клиент SMTP продолжит сеанс связи без использования шифрования, что, в свою очередь, позволит стороннему сервису получить полный доступ к заголовкам и телу передаваемого письма

Вне зависимости от того, с какой целью производится вмешательство в почтовый трафик (а это может быть и «благая» цель типа фильтрации спама или защиты от вирусов) подобное поведение называется MITM Downgrade Attack или атакой «человек посередине»

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

Сервер находился на территории Кипра, была весна 2013 года и обострение паранойи. . .

Безопасная отправка почты

Начальная настройка

Большинство серверов в интернете не предполагают приема почты, а только отправку (уведомления о происходящих событиях, рассылки и т.д.). Тем не менее достаточно часто можно встретить «декларирование» возможности приема почты и (хотя существенно реже) сервера в состоянии open relay — почтовый сервер, открытый для пересылки почты от любых отправителей любым получателям

Поскольку мы не предполагаем прием почты из внешнего мира, то первым шагом имеет смысл отключить декларирование данной возможности на уровне Postfix:

inet_interfaces = loopback-only

Параметр конфигурации inet_interfaces может принимать следующие значения:

  • all — прослушивать все адреса на всех интерфейсах (это значение по умолчанию)
  • loopback-only — прослушивать адреса только на loopback интерфейсах (обычно это 127.0.0.1 и [::1])
  • Список IP адресов для прослушивания через разделитель (пробел или запятая), где IPv6 адреса указываются в квадратных скобках

Так же, имеет смысл явно указать список доверенных сетей:

mynetworks = 127.0.0.0, [::1]

Параметр конфигурации mynetworks задает список сетей или хостов через разделитель, которые могут отправлять почту без авторизации

Отправка с использованием TLS

Для организации защищенного канала связи в Postfix используется TLS — безопасный протокол транспортного уровня. Для того, чтобы почтовый сеанс связи между двумя хостами производился по защищенному каналу связи должны быть выполнены следующие условия:

  • Отправитель и получатель должны поддерживать расширения SMTP (ESMTP — SMTP Service Extensions, RFC 1869)
  • Отправитель и получатель должны поддерживать расширение STARTTLS (SMTP Service Extension for Secure SMTP over Transport Layer Security, RFC 3207)
  • В сеансе связи получатель должен декларировать поддержку расширения STARTTLS, а отправитель им воспользоваться

На сегодняшний день поддержку ESMTP имеют подавляющее большинство почтовых серверов (стандарт датирован 1995-м годом), а на оставшиеся два пункта следует обратить более пристальное внимание

Очевидно, что при регистрации или подписке на рассылку, пользователь будет использовать почтовый адрес, зарегистрированный на каком-либо популярном публичном почтовом сервисе. Используя базу данных из почти 60 тыс. адресов одного веб-проекта я получил следующую статистику по предпочтениям пользователей почты в Российском сегменте интернета:

домен %
mail.ru 55,53
yandex.ru 22,06
gmail.com 9,56
rambler.ru 5,97
ukr.net 1,85
yahoo.com 1,53
прочие 5,50

Здесь в долю домена @mail.ru входят (помимо самого @mail.ru) @inbox.ru, @list.ru, @bk.ru. В долю домена @yandex.ru входят @ya.ru, все региональные поддомены @yandex и @narod.ru. К «прочим» отнесены все почтовые домены, набравшие менее 1% в общем распределении. На разных проектах цифры, естественно, могут отличаться, однако сохранится общая тенденция в списке лидеров

Попробуем посмотреть на уровень поддержки STARTTLS лидерами рынка:

домен starttls
mail.ru нет
yandex.ru нет
gmail.com да
rambler.ru нет
ukr.net нет
yahoo.com нет
прочие 28% да

Другими словами, перед нами возникает очень интересный факт — поддержка TLS в реальной жизни может быть обеспечена всего для 11% получателей и с очень большой вероятностью сервер получателя будет находиться за пределами Российской Федерации (например, сервис Google Apps — почта для домена). Тут есть над чем призадуматься. . .

В сложившейся ситуации отношения крупных игроков к безопасности своих пользователей все, что мы можем сделать — это вести разъяснительную работу среди посетителей своих сайтов и сервисов, предоставляя им актуальную информацию и рекомендации по обеспечению собственной безопасности. А пока, позаботимся о тех 11% счастливчиков

Базовую конфигурацию для использования TLS при отправке сообщений можно представить в виде:

tls_high_cipherlist = HIGH:@STRENGTH
smtp_tls_loglevel = 1
smtp_tls_security_level = may
smtp_tls_session_cache_database = btree:/var/lib/postfix/smtp_tls_cache
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_tls_protocols = !SSLv2, !SSLv3
smtp_tls_ciphers = high
smtp_tls_exclude_ciphers = aNULL, MD5, CAMELLIA

Здесь:

  • tls_high_cipherlist — список шифров с «высоким» уровнем защиты. Значение HIGH:@STRENGTH выбирает шифры с высоким уровнем защиты и упорядочивает их в порядке уменьшения стойкости шифра. Подробнее см. документацию по OpeenSSL
  • smtp_tls_loglevel — уровень детализации ведения лога соединений сеансов TLS (0-4, нет-отладка). Здесь включаем минимальный уровень логгирования в котором будет записываться информация о факте использования защищенного соединения
  • smtp_tls_security_level — уровень безопасности при установке защищенного соединения. Значение may предполагает использование TLS при поддержке второй стороной игнорируя дополнительные проверки
  • smtp_tls_session_cache_database — имя файла базы для кэширования информации о TLS сессиях (для FreeBSD может иметь значение, например, btree:/var/db/postfix/smtp_tls_cache)
  • smtp_tls_CAfile — файл со списком сертификатов доверенных центров сертификации (для FreeBSD обычно /usr/local/share/certs/ca-root-nss.crt)
  • smtp_tls_protocols — список поддерживаемых протоколов. Протоколы SSLv2 и SSLv3 в современном мире практически никто не использует, однако, если вы хотите дать шанс старому или неверно настроенному серверу, можно оставить этот параметр по умолчанию — !SSLv2
  • smtp_tls_ciphers — минимальный уровень шифрования. Значение high берется из списка шифров заданных в tls_high_cipherlist
  • smtp_tls_exclude_ciphers — список исключаемых шифров. Исключаются шифры без аутентификации (aNULL) и слабой (MD5) аутентификацией. Шифр CAMELLIA исключен ввиду его низкой распространенности (количества HIGH шифров более чем достаточно, чтобы сделать выбор)

В данной конфигурации ключевым параметром является smtp_tls_security_level. Поскольку использование сертификатов, подписанных доверенными центрами сертификации, может быть достаточно дорого (несмотря на то, что Go Daddy предлагает приобрести сертификат всего за 6$), многие почтовые сервера используют самоподписанные сертификаты. Использование самоподписанных сертификатов не влияет на стойкость шифрования, однако, такие сертификаты не проходят проверку при использовании более строгого уровня безопасности — в этой ситуации помогает значение параметра may, который позволяет устанавливать защищенное соединение даже если сертификат сервера получателя не пошел проверку

Усиленный TLS

В базовой конфигурации TLS не производится никаких дополнительных проверок сертификатов, которые предоставляются сервером получателя, однако, существует как минимум один крупный почтовый сервис, предоставляющий возможность приема почты с использованием TLS и имеющий сертификат, подписанный доверенным центром сертификации — речь идет о сервисе Gmail

Для усиления уровня безопасности в зависимости от домена получателя можно воспользоваться параметром smtp_tls_policy_maps в котором указать сопоставление домена и требуемого уровня безопасности:

smtp_tls_policy_maps = hash:/etc/postfix/tls_policy

Сам файл tls_policy с картой сопоставлений для gmail будет выглядеть следующим образом:

gmail.com secure match=.google.com:google.com:.gmail.com:gmail.com

Здесь, для домена получателя gmail.com задается максимальный уровень проверки сертификата (secure) и сертификат должен быть выписан для поддомена или домена google.com или gmail.com. На данный момент, сертификат, предоставляемый MX серверами gmail.com, выписан на домен mx.google.com, однако, поскольку нельзя гарантировать, что этот же сертификат будет использоваться и в дальнейшем, принятие любого сертификата для домена или поддомена компании является разумным компромиссом между надежностью доставки и безопасностью

После изменения текстового варианта карты, необходимо создать саму базу данных выполнив команду:

# postmap /etc/postfix/tls_policy

Для получения имени домена, для которого выписан сертификат того или иного почтового сервера, необходимо получить список его MX серверов:

# dig gmail.com mx


;; ANSWER SECTION:
gmail.com. 3600 IN MX 5 gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 10 alt1.gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 20 alt2.gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 30 alt3.gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 40 alt4.gmail-smtp-in.l.google.com.

После чего, выбрав требуемый сервер, получить информацию о сертификате:

# openssl s_client -connect gmail-smtp-in.l.google.com:25 -starttls smtp


Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=mx.google.com
i:/C=US/O=Google Inc/CN=Google Internet Authority
1 s:/C=US/O=Google Inc/CN=Google Internet Authority
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority

Проверка валидности сертификата производится с указанием имени файла, содержащего список сертификатов доверенных центров сертификации:

$ openssl s_client -CAfile /etc/ssl/certs/ca-certificates.crt \
 -connect gmail-smtp-in.l.google.com:25 -starttls smtp


SSL-Session:

Verify return code: 0 (ok)

Уведомления о невозможности доставки

В случае, когда сообщение не может быть доставлено получателю в течении определенного времени или возникает фатальная ошибка (например, сервер получателя сообщил, что пользователя не существует), отправителю генерируется bounce сообщение, информирующее о невозможности доставки, содержащее в себе исходное сообщение. В случае, если bounce сообщение невозможно доставить отправителю, может генерироваться double-bounce (или 2bounce) сообщение, которое обычно отправляется администратору почтового сервера (по умолчанию на адрес postmaster), которое так же может содержать тело исходного сообщения или другую конфиден циальную информацию. Если double-bounce сообщение невозможно доставить, оно удаляется

Время жизни сообщения в очереди по умолчанию составляет пять дней и контролируется параметром maximal_queue_lifetime. В современных реалиях никто не ждет важного сообщения столь длительный срок (а по моему опыту, если письмо не может быть отправлено в течении шести–восьми часов, то вероятность его успешной отправки в дальнейшем минимальна)

Время жизни bounce сообщения в очереди по умолчанию составляет так же пять дней и контролируется параметром bounce_queue_lifetime. Обычно, веб-приложения отправляют сообщения с адресов вида noreply@example.com, которые на практике никем не просматриваются (в лучшем случае, это реально существующий ящик, который постепенно забивается уведомлениями)

Типы сообщений, которые отправляются на адрес postmaster (если не указано иного в параметрах bounce_notice_recipient, 2bounce_notice_recipientdelay_notice_recipient и error_notice_recipient) контролируются параметром notify_classes и по умолчанию отправляются сообщения только о программных ошибках или нехватке ресурсов (на адрес указанный в параметре error_notice_recipient)

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

Ящик, который указан в качестве обратного адреса в сообщении, обязан существовать. Это требование связано с тем, что до сих пор существуют почтовые сервера, которые проверяют существование адреса отправителя через попытку обратного соединения. Однако, если данный ящик никем и никогда не просматривается, bounce сообщения на него уходить не должны. Этого поведения можно достичь через карту «виртуальных» адресов virtual_alias_maps:

virtual_alias_maps = hash:/etc/postfix/virtual

Где содержимое /etc/postfix/virtual представляет собой запись вида:

noreply@example.com devnull

А алиас devnul в файле локальных алиасов /etc/aliases представляет собой запись вида:

devnull: /dev/null
noreply: devnull

Сами же карты локальных алиасов задаются в конфигурации в виде:

alias_maps = hash:/etc/aliases
alias_database = $alias_maps
local_recipient_maps = $alias_maps

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

# postalias /etc/aliases
# postmap /etc/postfix/virtual

Подобная конфигурация будет отправлять все сообщения, адресованные noreply@example.com (в т.ч. и bounce-сообщения), прямиком в «шредер» /dev/null

Сообщения о наличии программных ошибок и нехватке ресурсов, конечно же, желательно получать раньше, чем начнут беспокоиться пользователи, однако лучше это делать через специализированные средства мониторинга — в этом случае рекомендуется подавить уведомления:

notify_classes =

Все прочие сообщения, адресованные локальному пользователю root (явно или через цепочку в /etc/aliases), имеет смысл так же или подавлять (рискуя не узнать вовремя что-то важное и надеясь на средства мониторинга) или дублировать на два и более адресов в различных безопасных доменах

Anton Batenev

Часть 2Часть 3

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