跳转到主要内容

于 2025年04月22日 摘录自 Postfix Backscatter Howto

概述

本文件描述了需要 Postfix 2.0 或更高版本的功能。

本文件涵盖的主题:

示例使用 Perl 兼容正则表达式(Postfix pcre:表格),但同时也提供了 POSIX 正则表达式的翻译(Postfix regexp:表格)。PCRE 主要因其实现速度更快而被推荐。

什么是回弹邮件?

当垃圾邮件发送者或蠕虫使用伪造的发件人地址发送邮件时,无辜的网站会被无法投递的邮件通知淹没。这被称为反向散射邮件。使用 Postfix 时,当日志文件中出现如下内容时,您就知道自己成为了反向散射的受害者:

Dec 4 04:30:09 hostname postfix/smtpd[58549]: NOQUEUE: reject:
RCPT from xxxxxxx[x.x.x.x]: 550 5.1.1 <;[email protected]>;:
收件人地址被拒绝:用户不存在;发件人=<;>;
收件人=<;[email protected]>; 协议=ESMTP helo=<;zzzzzz>;

您看到的是大量"用户未知"错误,其中包含"from=<;>;"。这些是互联网其他位置的MAILER-DAEMON发送的错误报告,涉及使用您域名中的虚假发件人地址发送的邮件。

如何阻止发送到随机收件人地址的回弹邮件?

如果您的机器收到发送到随机地址的回弹邮件,请按照以下说明配置 Postfix 以拒绝所有发送到不存在收件人的邮件:LOCAL_RECIPIENT_READMESTANDARD_CONFIGURATION_README 文档中所述的方式配置 Postfix 以拒绝所有发往不存在收件人的邮件。

如果您的系统运行的是 Postfix 2.0 及更早版本,请在 SMTP 服务器中禁用"在拒绝前暂停"功能。如果系统处于高负载状态,则不应浪费时间。

/etc/postfix/main.cf:
# Postfix 2.1 及更高版本无需此设置。
smtpd_error_sleep_time = 0
# Postfix 2.4 及更高版本无需此设置。
unknown_local_recipient_reject_code = 550

如何阻止退信邮件发送到真实收件人地址?

当退信邮件突破"未知收件人"屏障时,仍无需绝望。许多邮件系统会善意地在未送达通知中附上无法送达邮件的邮件头信息。这些邮件头包含可用于识别并阻止伪造邮件的信息。

使用伪造的邮件服务器信息阻止回弹邮件

尽管我的电子邮件地址是 "[email protected]",但所有邮件系统在 SMTP HELO 命令中都以 "hostname.porcupine.org" 进行自我声明。因此,如果返回的邮件包含类似以下的 Received: 消息头:

Received: from porcupine.org ...

那么我可以确定这几乎肯定是伪造的邮件(几乎;请参阅下一节中的例外情况)。由我的系统真正发送的邮件看起来像这样:

Received: from hostname.porcupine.org ...

出于相同原因,以下邮件头很可能是伪造的结果:

Received: from host.example.com ([1.2.3.4] helo=porcupine.org) ...
Received: from [1.2.3.4] (port=12345 helo=porcupine.org) ...
Received: from host.example.com (HELO porcupine.org) ...
Received: from host.example.com (EHLO porcupine.org) ...

一些伪造信息会以邮件服务器在 Received: 消息头中报告自身的方式出现。考虑到所有我的系统都使用 hostname.porcupine.org 作为邮件服务器名称,以下内容肯定是伪造的:

Received: by porcupine.org ...
Received: from host.example.com ( ... ) by porcupine.org ...

另一种常见的伪造迹象是 Message-ID: 头部。我的系统生成 Message-ID: 为 <;stuff@hostname.porcupine.org>;。以下是伪造的,尤其是第一个:

Message-ID: <;[email protected]>;
Message-ID: <;[email protected]>;

为了阻止此类回弹邮件,我使用了header_checksbody_checks模式,如下所示:

/etc/postfix/main.cfheader_checks = pcre:/etc/postfix/header_checks
body_checks = pcre:/etc/postfix/body_checks
/etc/postfix/header_checks:
# 不要缩进"if"和"endif"之间的模式。
if /^Received:/
/^Received: +from +(porcupine\.org) +/
拒绝在 Received: 标头中伪造的客户端名称: $1
/^Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
拒绝 Received: 头中伪造的客户端名称: $2
/^Received:.* +by +(porcupine\.org)\b/
拒绝 Received: 头中伪造的邮件服务器名称: $1
endif
/^Message-ID:.* <!&!/ DUNNO
/^Message-ID:.*@(porcupine\.org)/
拒绝在 Message-ID: 标头中伪造的域名: $1
/etc/postfix/body_checks:
# 不要缩进 "if" 和 "endif" 之间的模式。
if /^[> ]*Received:/
/^[> ]*Received: +from +(porcupine\.org) /
拒绝在 Received: 标头中伪造的客户端名称: $1
/^[> ]*Received: +from +[^ ]+ +\(([^ ]+ +[he]+lo=|[he]+lo +)(porcupine\.org)\)/
拒绝在 Received: 标头中伪造的客户端名称:$2
/^[> ]*Received:.* +by +(porcupine\.org)\b/
拒绝在 Received: 标头中伪造的邮件服务器名称:$1
endif
/^[>; ]*Message-ID:.* <;!&;!/ DUNNO
/^[>; ]*Message-ID:.*@(porcupine\.org)/
拒绝在 Message-ID: 标头中伪造的域名:$1

注释:

  • 示例使用了pcre:表主要用于提升速度;经过少量修改,您可以使用regexp:表,具体说明如下。
  • 示例已简化以供教学使用。实际中,我的正则表达式会列出多个域名,如 "(domain|domain|...)"。
  • "\." 字面匹配 "."。若不添加 "\","." 将匹配任何字符。
  • "\(" 和 "\)" 字面匹配 "" 和 ""。如果没有 "\","" 和 "" 将被视为分组运算符。
  • "\b"用于匹配单词的结尾。如果你使用 regexp: 表格,请指定 "[[:>;:]]"(在某些系统中应指定 "\>;";详情请参阅系统文档)。
  • "if /pattern/" 和 "endif" 可消除不必要的匹配尝试。请勿缩进 "if" 和 "endif" 之间的以 /pattern/ 开头的行!
  • 两个 "Message-ID:.* <;!&;!" 规则是针对某些版本的 Outlook Express 的临时解决方案,具体说明请参见下文的 注意事项 部分。

注意事项

  • Netscape Messenger(据报道,Mozilla 也是如此)发送的 HELO 名称与发件人地址的域名部分完全相同。如果您的系统中存在此类客户端,则上述模式会阻挡合法邮件。

    我的网络中仅有一台此类机器,为防止其邮件被阻挡,我已将其配置为以 [email protected] 发送邮件。在 Postfix 服务器上,一个规范映射将此临时地址转换为 [email protected]

    /etc/postfix/main.cfcanonical_maps = hash:/etc/postfix/canonical
    /etc/postfix/canonical:
    @hostname.porcupine.org @porcupine.org
    

    当然,这种方法仅在发送 HELO 命令的系统非常少,且从未需要向此类主机上的用户发送邮件时才实用。

    另一种方法是通过地址伪装从 "hostname.porcupine.org" 中移除主机名,具体请参阅 ADDRESS_REWRITING_README 文档。

  • 据报告,Outlook 2003(可能包括 Outlook Express 及其他版本)会根据是否请求 DSN(通过"选项"中的"请求此消息的送达回执"设置)生成截然不同的 Message-ID 头部。

    当请求 DSN 时,Outlook 2003 会使用以发件人域名结尾的 Message-ID 字符串:

    Message-ID: <;!&;! ...非常长的字符串... [email protected]>;
    

    其中 example.com 是 Outlook 账户设置中为用户指定的电子邮件地址中的域名部分。由于许多用户将电子邮件地址配置为 [email protected],启用 DSN 的消息将触发前一节中的 REJECT 操作。

    如果你有这样的客户端,你可以使用前一节中显示的两个 "Message-ID:.* <;!&;!" 模式来排除他们的 Message-ID 字符串。否则,你将无法使用两个反向散射规则来阻止伪造的 Message ID 字符串。当然,这个解决方法可能会在 Outlook 下次更新时失效。

阻止包含伪造发件人信息的回弹邮件

与许多人一样,我仍然保留着一些过去使用的域名下的邮箱地址。这些地址的邮件会被转发到我的当前地址。我收到的回弹邮件中,大部分声称来自这些地址。此类邮件显然是伪造的,且非常容易阻止。

/etc/postfix/main.cfheader_checks = pcre:/etc/postfix/header_checks
body_checks = pcre:/etc/postfix/body_checks
/etc/postfix/header_checks:
/^(From|Return-Path):.*\b(user@domain\.tld)\b/
拒绝伪造发件人地址在 $1: 头部: $2
/etc/postfix/body_checks:
/^[> ]*(From|Return-Path):.*\b(user@domain\.tld)\b/
拒绝伪造发件人地址在 $1: 头部: $2

注意事项:

  • 示例使用了pcre: 表,主要为了提高速度;经过少量修改,您可以使用regexp: 表,具体如下所述。
  • 该示例已简化以供教学使用。实际中,我的正则表达式会列出多个邮箱地址,如 "(user1@domain1\.tld|user2@domain2\.tld)"。
  • 在 "\b(user@domain\.tld)\b" 中使用的两个 "\b" 分别匹配单词的开头和结尾。如果您使用 regexp: 表格,请指定 "[[: <;:]][[:>;:]]"(在某些系统中应指定 "\<;\>;";具体请参阅系统文档)。
  • "\." 匹配 "." 的字面值。如果没有 "\","." 将匹配任何字符。

阻止包含其他伪造信息的回弹邮件

伪造的另一个迹象可以在 Received: 头中记录的 IP 地址中找到,该地址位于您的 HELO 主机或域名旁边。不过,使用此信息时需谨慎。部分邮件服务器位于网络地址转换器之后,无法看到真实的客户端 IP 地址。

阻止病毒扫描程序发送的回弹邮件

在消除所有易于识别的伪造邮件后,仍有一类回弹邮件无法被过滤,即病毒扫描软件发送的通知。不幸的是,部分病毒扫描软件并不知道病毒会伪造发件人地址。更糟糕的是,这些软件也不具备报告邮件投递问题的功能,因此无法通过上述方法识别伪造邮件。

识别病毒扫描器邮件是一个容易出错的过程,因为报告格式存在很大差异。以下仅是消息头部模式的一个小示例。要获取大量识别病毒通知邮件的头部和正文模式,请参阅https://web.archive.org/web/20100317123907/http://std.dkuug.dk/keld/virus/http://www.t29.dk/antiantivirus.txt

/etc/postfix/header_checks:
/^Subject: *您的邮件中含有病毒/ 丢弃病毒通知
/^Content-Disposition:.*VIRUS1_DETECTED_AND_REMOVED/
丢弃病毒通知
/^Content-Disposition:.*VirusWarning.txt/ 丢弃病毒通知

注意:这些文件自2004年以来未更新,仅可作为参考起点。

致病毒或垃圾邮件扫描程序运营商的请求:请勿通过向伪造的发件人地址发送回信来加剧问题。您只是在骚扰无辜的人。如果您必须将邮件退还给所谓的发件人,请务必返回完整的邮件头信息,以便发件人能够过滤掉明显的伪造内容。