Ошибка зеркалирования списка рассылки с последствиями для безопасности

Я обнаружил интересную и редкую ошибку, касающуюся того, как ответы на сообщения в рассылках, дублируемые в Discourse, классифицируются.

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

Представьте, что я подписан на две рассылки, A и B, используя свой адрес Gmail. У них может быть разная аудитория. Сервер рассылки отправляет одно сообщение тем, кто подписан на обе рассылки, но в строке «Кому» указано, что письмо отправлено в списки A и B одновременно. Это письмо приходит на мой адрес Gmail, который затем через фильтр пересылает копию на listA@mydiscourse.org.uk, при этом Gmail выбирает один из двух списков, указанных в строке «Кому». В Gmail настроена пересылка постов из A на listA@mydiscourse.org.uk, а постов из B — на listB@mydiscourse.org.uk. В Discourse созданы две категории с этими двумя разными входящими адресами для зеркального отображения (listA@mydiscourse.org.uk и listB@mydiscourse.org.uk).

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

После этого копия письма, отправленного мне на мой аккаунт Gmail, отображается в категории, связанной с входящим адресом listA@mydiscourse.org.uk, после того как Gmail переслал его.

Пока всё в порядке (за исключением того факта, что изначально от сервера рассылки пришло только одно письмо).

Теперь другой пользователь, который состоит только в рассылке B, отвечает на это письмо серверу рассылки. Сервер рассылки отправляет это сообщение всем подписчикам рассылки B. Оно приходит на мой адрес Gmail со строкой «Кому», указывающей, что оно отправлено в список B. Gmail пересылает его на listB@mydiscourse.org.uk.

Однако затем Discourse классифицирует его так, будто оно было отправлено на listA@mydiscourse.org.uk!

Таким образом, это раскрывает участникам списка A содержание сообщения участника списка B!

Я подозреваю, что Discourse игнорирует путь, по которому письмо попало в систему, и классифицирует его на основе флагов идентификатора сообщения в заголовках, тем самым обходя путь поступления.

В исходном письме, отправленном в списки A и B, содержится один идентификатор сообщения, и я подозреваю, что классификация происходит исключительно на основе этого идентификатора.

Если бы исходный отправитель написал два отдельных письма в список A и список B, эта проблема не возникла бы, так как у двух сообщений были бы свои собственные идентификаторы.

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

Действительно, или:

Я считаю, что это как раз предмет данного бага. Это должно быть исправлено в системе списков рассылки, а не в Discourse.

Не уверен, что я хорошо объяснил, так как не понимаю, почему Discourse помещает сообщение в неправильную категорию. Создается впечатление, что он не учитывает входящий маршрут (который должен иметь приоритет), а ориентируется только на message-id. Если одно и то же электронное письмо отправляется одновременно в два списка рассылки, у него будет один message-id, но два независимых списка рассылки, два адреса в поле «To:» и два адреса в поле «Reply-To:» (в каждом из сообщений будет показан свой адрес в зависимости от того, в какой список оно было отправлено).

Если происходит именно так, то что будет, если кто-то переместит тему из категории, являющейся зеркалом списка рассылки, в другое место на Discourse, но затем продолжат приходить ответы (через список рассылки)? Будет ли Discourse добавлять эти ответы в категорию, связанную со списком рассылки (даже если исходное сообщение было перемещено), или все ответы каким-то образом окажутся в категории, куда была перемещена тема?

Может ли Discourse корректно обрабатывать наличие двух сообщений в двух разных категориях одновременно, если у них одинаковый message-id, но разные адреса в поле «To:»?

Независимо от того, как Discourse обрабатывает эту ситуацию, Message-ID должен быть уникальным. На мой взгляд, когда сервер рассылки отправляет сообщение в рассылку, он создаёт новое воплощение этого сообщения, и поэтому должен перезаписывать его Message-ID.

Кроме того, я не понимаю, как исходное сообщение могло подходить как для участников списка A, так и для списка B, но ответ участника списка B не мог быть показан участникам списка A. Если эти аудитории настолько различны, почему пользователь отправляет одинаковые сообщения обоим спискам одновременно?

Возможно, существует хорошее техническое решение для этого, и, возможно, Discourse можно изменить так, чтобы он обрабатывал это корректно, но я считаю это действительно странным частным случаем в технологии, которой уже 36 лет.

Можно утверждать, что, поскольку сообщение одинаковое, у него есть один идентификатор сообщения (message-id). На самом деле, в рассматриваемом примере оригинальный автор написал сообщение в Gmail (который присвоил message-id), и оно было отправлено на два адреса рассылки на одном сервере. Это не редкость; например, это может быть пресс-релиз, общий для менеджеров и клиницистов, каждый из которых состоит в своей собственной рассылке. В пресс-релизе может быть медицинский вопрос, требующий обсуждения, поэтому клиницист может ответить (при этом адрес Reply-to: будет установлен на рассылку клиницистов, даже если в заголовке To: всё ещё будут указаны обе рассылки). Аналогично, менеджеры могут захотеть обсудить свою точку зрения на новости. Они ответят, и будет использован соответствующий заголовок Reply-to:, отражающий рассылку менеджеров. Если бы один из участников использовал адреса в поле To:, нажав «Ответить всем», то было бы сгенерировано сообщение об ошибке доставки (bounce message) от рассылки, в которой он не состоял.

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

Это может означать, что менеджеры, например, смогут видеть сообщения клиницистов, если ответ клинициста на рассылку клиницистов появится в зеркале рассылки менеджеров из-за того, как Discourse обрабатывает сообщения.

Решением могло бы быть проверка каждого адреса в заголовке To: входящих сообщений на соответствие адресу зеркала рассылки в настройках каждой категории. Если совпадение найдено, копия сообщения могла бы быть опубликована в каждой подходящей категории, и в разных категориях потенциально могло бы появиться несколько копий одного и того же сообщения. Тогда, если придут какие-либо ответы, можно было бы использовать message-id, как это делается сейчас, но только если строка To: также соответствует категории.

Может ли одним из решений стать возможность переписывать заголовок Message-ID так, чтобы он отражал исходный идентификатор, созданный почтовым клиентом автора, плюс дополнительный идентификатор, например, адрес в поле To: рассылки, который Discourse уже ищет? Это означало бы, что если сообщение поступит из рассылки B, но будет ссылаться на заголовок Message-ID, который также встречается в сообщении из рассылки A, то Discourse правильно его классифицирует?

Например: исходное письмо, отправленное одновременно в две рассылки A и B, имеет уникальный Message-ID ‘1234567890gmail’.

Discourse получает две копии, пересланные на общий входящий адрес Discourse. Первое сообщение классифицируется на основе поля To:, указывающего на список A, и к его Message-ID Discourse добавляет дополнительные символы, превращая его в ‘1234567890gmailListA’. Второе сообщение не классифицируется, так как выглядит как дубликат. (Полагаю, это текущее поведение).

Участник списка B отвечает, используя адрес в поле To: для списка B. Discourse получает копию и замечает заголовок In-Reply-To, содержащий ‘1234567890gmail’. Видя, что письмо пришло с полем To:, указывающим на список B, Discourse добавляет символы в конец, чтобы заголовок In-Reply-To выглядел как ‘1234567890gmailListB’. Затем он классифицирует это как новое сообщение в правильной категории, так как оно больше не связано с сообщением, классифицированным по списку A.

Такой подход может работать и при переносе архивов, где один и тот же Message-ID может встречаться в нескольких рассылках, если пользователь отправил сообщение в несколько списков одновременно. В момент импорта ко всем полям Message-ID (и In-Reply-To) можно добавлять уникальный текст, связанный с именем рассылки, чтобы предотвратить своего рода перекрестные помехи между архивами при индексации сообщений.