Безопасность почты Flask не соответствует требованиям безопасности Microsoft Outlook? - PullRequest
0 голосов
/ 08 октября 2018

У нас есть веб-приложение, которое отправляет электронные письма клиентам, и веб-приложение использует для этого почтовую инфраструктуру Flask.Около 2 недель назад нашему веб-приложению не удалось отправить электронные письма клиентам и нашей группе людей.Мы используем Outlook 365 в качестве отправителя.

Удаленный сервер возвратил '554 5.6.0 Поврежденное содержимое сообщения;STOREDRV.Deliver.Exception: ConversionFailedException;Не удалось обработать сообщение из-за постоянного исключения с сообщением Преобразование содержимого. Поврежденное резюме содержимого TNEF.ConversionFailedException: Преобразование содержимого: поврежденное краткое содержание TNEF.[Stage: PromoteCreateReplay] 'Заголовки исходного сообщения:

Это сообщение об ошибке, которое отправитель получает после получения инструкции отправить электронное письмо.Мы связались с нашим администратором Office 365, и Microsoft сказала ему, что безопасность нашего веб-приложения не соответствует требованиям / протоколу Microsoft.

Вопрос в том, использует ли почта Flask более старый протокол безопасности или конфигурацию, которая плохо работает с Microsoft Outlook?

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Сообщение об ошибке Outlook.com / Office365 не очень полезно, поскольку может указывать на любое количество проблем.Это указывает на то, что почтовые серверы Microsoft были недовольны каким-либо аспектом упаковки электронной почты (заголовки, вложения и т. Д.), И их анализаторы где-то ошиблись.В противном случае их сообщение об ошибке почти бесполезно в том, что оно предоставляет.Я считаю, что утверждение о том, что это проблема безопасности , является бессмысленным;Flask-Mail использует проверенные пакеты стандартной библиотеки Python email и smtplib для отправки электронной почты через зашифрованное соединение TLS.

Для Flask-Mail в Heroku я вместо этого отследил проблему до заголовка Message-ID, который генерируется на машинах Heroku Dyno.Проблема не ограничивается Heroku, однако вы можете увидеть это на любом хосте с длинным именем хоста .Типичное имя хоста Heroku dyno начинается с полного UUID, плюс еще 5 компонентов или около того, например, aaf39fce-569e-473a-9453-6862595bd8da.prvt.dyno.rt.heroku.com.

Это имя хоста используется в заголовке идентификатора сообщения, который создается для каждого электронного письма.Пакет Flask-Mail использует стандартную email.utils.make_msgid() функцию для генерации заголовка, которая по умолчанию использует текущее имя хоста.В результате получается заголовок идентификатора сообщения, например:

Message-ID: <154810422972.4.16142961424846318784@aaf39fce-569e-473a-9453-6862595bd8da.prvt.dyno.rt.heroku.com>

Это строка длиной 110 символов.Это небольшая проблема для заголовков электронной почты, потому что в RFC для электронной почты указано, что заголовки должны ограничиваться 78 символами.Однако есть способы обойти это;для значений заголовков длиннее 77 символов можно использовать положения в заголовках от RFC 5322 до fold .При свертывании можно использовать несколько RFC 2047 кодированных слов в нескольких строках.Это то, что происходит здесь, заголовок электронного письма выше становится

Message-ID: =?utf-8?q?=3C154810422972=2E4=2E16142961424846318784=40aaf39fce-?=
 =?utf-8?q?569e-473a-9453-6862595bd8da=2Eprvt=2Edyno=2Ert=2Eheroku=2Ecom=3E?=

, который, будучи 78 и 77 символами, теперь соответствует стандарту MIME электронной почты.

Все это мне кажетсябыть совместимым со стандартами и действительным методом обработки почтовых заголовков.Или, по крайней мере, то, что другие почтовые провайдеры переносят и обрабатывают должным образом, но почтовые серверы Microsoft не имеют этого.Им действительно не нравится вышеуказанный заголовок Message-ID, закодированный в RFC2047, и они пытаются обернуть тело в прикрепленный файл TNEF winmail.dat.Это не всегда работает, поэтому вы получите очень загадочное 554 5.6.0 Поврежденное сообщение Сообщение об ошибке.Я считаю это ошибкой Microsoft;Я не уверен на 100%, что RFC электронной почты позволяют складывать заголовок Message-ID с использованием закодированных слов, но MS обрабатывает ошибку, отправляя получателю бессмысленную ошибку, а не отклоняет сообщение, когда получение просто ужасно.

Вы можете установить альтернативную политику электронной почты для использования Flask-Mail, задав глобальный модуль flask_mail.message_policy, или мы можем сгенерировать другой идентификатор сообщения.

Политики электронной почты доступны только в том случае, если вы используете Python 3.3 или более поздней версии, но именно объект политики обрабатывает свертывание, что позволяет нам изменять способ обработки Message-ID и других заголовков идентификатора RFC 5322.Вот подкласс, который не свернет заголовок Message-ID;на самом деле стандарт позволяет использовать до 998 символов в одной строке, и этот подкласс использует это ограничение только для этого заголовка:

import flask_mail
from email.policy import EmailPolicy, SMTP

# Headers that contain msg-id values, RFC5322
MSG_ID_HEADERS = {'message-id', 'in-reply-to', 'references', 'resent-msg-id'}

class MsgIdExcemptPolicy(EmailPolicy):
    def _fold(self, name, value, *args, **kwargs):
        if (name.lower() in MSG_ID_HEADERS and
            self.max_line_length < 998 and
            self.max_line_length - len(name) - 2 < len(value)
        ):
            # RFC 5322, section 2.1.1: "Each line of characters MUST be no
            # more than 998 characters, and SHOULD be no more than 78
            # characters, excluding the CRLF.". To avoid msg-id tokens from being folded
            # by means of RFC2047, fold identifier lines to the max length instead.
            return self.clone(max_line_length=998)._fold(name, value, *args, **kwargs)
        return super()._fold(name, value, *args, **kwargs)

flask_mail.message_policy = MsgIdExcemptPolicy() + SMTP

В Python 2.7 или Python 3.2 или более поздней версии вам придется прибегнуть к заменезаголовок Message-Id, просто заново сгенерируйте заголовок с жестко закодированным доменным именем:

from flask import current_app
from flask_mail import Message as _Message

# set this to your actual domain name
DOMAIN_NAME = 'example.com'

class Message(_Message):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # work around issues with Microsoft Office365 / Outlook.com email servers
        # and their inability to handle RFC2047 encoded Message-Id headers. The
        # Python email package only uses RFC2047 when encoding *long* message ids,
        # and those happen all the time on Heroku, where the hostname includes a
        # full UUID as well as 5 more components, e.g.
        # aaf39fce-569e-473a-9453-6862595bd8da.prvt.dyno.rt.heroku.com
        # The work-around is to just use our own domain name, hard-coded, but only
        # when the message-id length exceeds 77 characters (MIME allows 78, but one
        # is used for a leading space)
        if len(self.msgId) > 77:
            domain = current_app.config.get('MESSAGE_ID_DOMAIN', DOMAIN_NAME)
            self.msgId = make_msgid(domain=domain)

Затем вы использовали бы вышеупомянутый класс Message вместо класса flask_mail.Message(), и онсоздаст более короткий заголовок Message-ID, который не будет конфликтовать с проблемными парсерами заголовков Microsoft.

Я подал отчет об ошибке в проекте Python для отслеживания обработки токенов msg-id, как я подозреваю, это действительно должно быть решено там.

0 голосов
/ 09 октября 2018

Электронная почта, использующая старый протокол безопасности или конфигурацию, может хорошо работать с Microsoft Outlook.

Что касается ошибки «554», вы можете сослаться на эту ссылку:

E-mailNDR

...