跳转到主要内容

于 2025年04月22日 摘录自 TLS Forward Secrecy in Postfix

警告

前向保密无法防范主动攻击,例如伪造的DNS响应或伪造的TLS服务器证书。如果此类攻击构成威胁,则SMTP客户端需要以足够安全的方式验证远程SMTP服务器。例如,通过公共密钥或证书的指纹(CA或叶证书)。传统PKI依赖于多个受信任的第三方,并容易被国家资助的攻击者破坏。

概述

Postfix自版本2.2起支持TLS网络通信的前向保密。此支持是从Lutz Jänicke的"Postfix TLS补丁"中采用的,适用于较早版本的Postfix。本文将重点介绍 Postfix SMTP 客户端和服务器中的 TLS 前向保密。有关 Postfix TLS 支持的通用描述,请参阅 TLS_README

本文涵盖的主题:

什么是前向保密

"前向保密"(或有时称为"完美前向保密")是指一种安全协议,其中当双方使用的长期密钥被泄露时,过去通信的机密性不会受到影响。

前向保密通过使用不保存的、具有密码学强度的随机数来协商会话密钥,并使用长期认证密钥对交换内容进行签名来实现。长期密钥的后续披露允许从该点起冒充密钥持有者,但无法恢复先前流量,因为在前向保密中,被丢弃的随机密钥协商输入无法被攻击者获取。

前向保密仅在以下条件下"完美":即使对资金最充足的攻击者而言,对密钥协商算法的暴力攻击也难以实现,且双方使用的随机数生成器足够强大。否则,前向保密仅使攻击者面临破解密钥协商协议的挑战,这可能极为耗时,但对于价值足够高的会话仍可能实现。因此,前向保密对批量监视的有效性施加了成本限制,恢复所有过去的流量通常不可行,甚至在使用足够强大的密钥协商方法时,恢复单个会话也可能不可行。

TLS中的前向保密

SSL协议的早期实现不提供前向保密(部分实现仅通过人工弱化的"出口"密码套件提供,但本文不考虑此类实现)。客户端向服务器发送一个用服务器RSA公钥加密的随机"预主密钥"。服务器用其私钥解密该密钥,并将其与明文交换的其他数据结合生成会话密钥。若攻击者获取了服务器的私钥,可在任何后续时间执行相同的计算。

TLS协议的后续版本引入了支持前向保密性的密码套件,其中客户端与服务器基于短暂密钥交换协议进行密钥交换。使用这些新密码套件加密的会话不会因长期认证密钥的未来泄露而被破解。

用于前向保密的关键交换算法要求 TLS 服务器指定适当的"参数",包括一个数学"组"及其组内称为"生成元"的元素。目前,与 PFS 兼容的"组"有两种变体:

  • FFDHE: 有限域迪菲-赫尔曼临时密钥交换组(也称为EDH或DHE)。服务器需要配置一个足够大的素数和一个对应的"生成元"。素数和生成元的标准选择在RFC7919中指定,可在TLS 1.3协议中使用,服务器和客户端协商支持的选择。在 TLS 的早期版本(1.0 至 1.2)中,当执行 FFDHE 密钥交换时,服务器会单方面选择素数和生成元。
  • EECDH: 这是"临时椭圆曲线 Diffie-Hellman"(也简写为 ECDHE)的缩写。EECDH 在计算成本较低的情况下提供比 FFDHE 更高的安全性。密码学中使用的椭圆曲线通常通过一个"名称"来标识,该名称代表一组已知的参数值。正是这些"命名曲线"(或在证书中关联的 ASN.1 对象标识符)在 TLS 协议中被使用。当使用 EECDH 密钥交换时,作为 TLS 握手的一部分,会协商一个双方支持的命名曲线。

Postfix SMTP 服务器中的前向保密

Postfix ≥ 2.2 SMTP 服务器在默认配置中支持前向保密。如果远程 SMTP 客户端偏好包含前向保密性的密码套件,则即使服务器长期认证密钥在后续被泄露,服务器与客户端之间的通信仍可抵御解密。

大多数远程 SMTP 客户端现在支持前向保密性(自 TLS 1.3 起为唯一选项),但部分客户端可能偏好不包含前向保密性的密码套件。Postfix ≥ 2.8 服务器可通过设置 "tls_preempt_cipherlist = yes" 覆盖客户端的偏好。

FFDHE 服务器支持

Postfix ≥ 3.1 默认支持 2048 位素数 FFDHE,无需额外配置。您也可以生成自己的 FFDHE 参数,但这并非必要且不再推荐。详情请参阅 快速入门 部分。

Postfix ≥ 3.8 支持 OpenSSL ≥ 3.0 中的有限域 Diffie-Hellman 短暂密钥交换(FFDHE)密钥交换组协商 API。FFDHE 组在 TLS 1.3 及更高版本中由客户端和服务器显式协商。在较早的 TLS 版本中,服务器单方面选择组。候选 FFDHE 组的列表可通过 "tls_ffdhe_auto_groups" 配置,该设置可在服务器和客户端上用于选择支持的组的优先级列表(优先级最高的组优先)。默认列表适用于大多数用户。"tls_eecdh_auto_curves"和"tls_ffdhe_auto_groups" 中的任一选项可设置为空,这将在 OpenSSL 3.0 搭配 TLS 1.3 时禁用 EC 或 FFDHE 密钥交换。不过,若所有 EC 曲线均被禁用或未包含最常用曲线,兼容性将显著下降。

EECDH 服务器支持

从 Postfix 3.2 和 OpenSSL 1.0.2 开始,服务器和客户端均启用了支持的 EECDH 曲线范围,并在 TLS 握手过程中协商一个合适的相互支持的曲线。支持的曲线列表可通过 "tls_eecdh_auto_curves" 参数进行配置。在 TLS 1.2 中,服务器需要将 "smtpd_tls_eecdh_grade" 的设置保留为默认值 "auto"(之前显式指定单一曲线级别的选项已废弃)。在 TLS 1.3 中,"smtpd_tls_eecdh_grade" 参数不再使用,曲线选择将无条件协商。

Postfix SMTP 客户端的前向保密

Postfix ≥ 2.2 的 SMTP 客户端在默认配置中支持前向保密。所有受支持的 OpenSSL 版本均支持 FFDHE 和 EECDH 密钥交换。如果远程 SMTP 服务器支持包含前向保密功能的密码套件(且未覆盖 SMTP 客户端的密码偏好),则服务器与客户端之间的通信即使在服务器长期认证密钥被 后续 泄露的情况下也能抵御解密。前向保密在 TLS 1.3 中始终启用。

Postfix ≥ 3.2 支持 OpenSSL ≥ 1.0.2 的曲线协商 API。候选曲线列表可通过 "tls_eecdh_auto_curves" 配置参数修改,该参数可用于在 Postfix SMTP 服务器和 SMTP 客户端上选择优先级排序的曲线列表(优先级最高者优先)。默认列表适用于大多数用户。

Postfix ≥ 3.8 支持 OpenSSL ≥ 3.0 的有限域 Diffie-Hellman 短暂(FFDHE)密钥交换组协商 API。FFDHE 候选组列表可通过 "tls_ffdhe_auto_groups" 配置,用于在服务器和客户端上选择一个按优先级排序的受支持组列表(最优选在前)。默认列表适用于大多数用户。

Postfix SMTP 客户端默认密钥套件列表已正确排序,优先选择支持前向保密(EECDH 和 FFDHE)的密钥套件,而非不支持前向保密的类似密钥套件。强烈建议管理员不要修改密钥套件列表定义。

快速入门,简易配置

EECDH 客户端支持(Postfix ≥ 3.2 且 OpenSSL ≥ 1.1.1)

此功能开箱即用,无需额外配置。

Postfix ≥ 3.2 支持 OpenSSL ≥ 1.0.2 的曲线协商 API。候选曲线列表可通过配置参数 "tls_eecdh_auto_curves" 进行修改,该参数可用于在 Postfix SMTP 服务器和 SMTP 客户端上选择优先级排序的曲线列表(优先级最高者优先)。默认列表适用于大多数用户。

EECDH 服务器支持(Postfix ≥ 3.2 且 OpenSSL ≥ 1.1.1)

此功能开箱即用,无需额外配置。

Postfix ≥ 3.2 支持 OpenSSL ≥ 1.0.2 的曲线协商 API。候选曲线列表可通过 "tls_eecdh_auto_curves" 配置参数修改,该参数可用于在 Postfix SMTP 服务器和 SMTP 客户端上选择优先级排序的曲线列表(优先级最高者优先)。默认列表适用于大多数用户。

FFDHE 客户端支持(Postfix ≥ 3.2,OpenSSL ≥ 1.1.1)

在 Postfix 3.8 之前或 OpenSSL 3.0 之前,FFDHE 在 TLS 1.2 或更早版本中可直接使用,无需额外配置。唯一可行的操作(不推荐)是禁用所有 "kDHE" 密码套件,这将同时禁用 TLS 1.2 及更早版本中的 FFDHE 密钥交换。

在 OpenSSL 1.1.1 中,FFDHE 不支持 TLS 1.3,因为 TLS 1.3 仅使用 EECDH 密钥交换。对 TLS 1.3 的 FFDHE 支持在 OpenSSL 3.0 中添加。在 OpenSSL 3.0 和 Postfix 3.8 中,TLS 1.3 FFDHE 组的列表可通过 "tls_ffdhe_auto_groups" 参数进行配置,该参数可设置为空以禁用 TLS 1.3 中的 FFDHE,或扩展以支持更多组。默认设置应适用于大多数用户。

FFDHE 服务器支持(Postfix ≥ 2.2,所有受支持的 OpenSSL 版本)

在 Postfix 3.8 之前或 OpenSSL 3.0 之前,FFDHE 在 TLS 1.2 或更早版本中"开箱即用",无需额外配置。当然可以(不建议)禁用所有"kDHE"密码套件,这将禁用 TLS 1.2 及更早版本中的 FFDHE 密钥交换。

Postfix 3.1 及更高版本中内置的默认 FFDHE 组为 2048 位。您可以选择生成非默认的 Postfix SMTP 服务器 FFDHE 参数,以可能提升对预计算攻击的安全性,但这并非必要或推荐。只需将 "smtpd_tls_dh1024_param_file" 保持为默认的空值。

启用 TLS 1.3 时可使用的 FFDHE 组集在 Postfix ≥ 3.8 和 OpenSSL ≥ 3.0 中可配置。默认设置为 "tls_ffdhe_auto_groups" 启用 RFC7919 中的 2048 和 3072 位组。若需更高的安全性,建议使用 EECDH。

如何查看连接是否具有前向保密性? ;

Postfix 可配置为报告协商的加密套件、对应的密钥长度以及远程对等证书或公钥验证状态。

  • 通过设置 "smtp_tls_loglevel = 1" 和 "smtpd_tls_loglevel = 1",Postfix SMTP 客户端和服务器将把 TLS 连接信息记录到 maillog 文件中。通用日志文件格式如下所示。使用 TLS 1.3 时,在密码套件名称和位数之后可能还会记录其他属性。

    postfix/smtp[进程ID]: 与主机.example.com[192.168.0.2]:25 建立了未受信任的 TLS 连接
    TLSv1 采用密钥名 cipher-name实际密钥大小/原始密钥大小 位)
    postfix/smtpd[进程ID]:与主机.example.com[192.168.0.2]建立了匿名TLS连接
    使用TLSv1和密码cipher-name实际密钥大小/原始密钥大小位)
    
  • 启用 "smtpd_tls_received_header = yes" 后,Postfix SMTP 服务器将在 Received: 头中以注释形式(括号内的文本)记录 TLS 连接信息。具体格式取决于smtpd_tls_ask_ccert设置。在TLS 1.3中,加密套件名称和位数之后可能还会记录其他属性。

    Received: from host.example.com (host.example.com [192.168.0.2])
    (using TLSv1 with cipher cipher-nameactual-key-size/raw-key-size bits))
    (Client CN "host.example.com", Issuer "John Doe" (not verified))
    接收:来自 host.example.com (host.example.com [192.168.0.2])
    (使用 TLSv1 协议,加密套件 cipher-nameactual-key-size/raw-key-size 位))
    (未请求客户端证书)
    

    TLS 1.3 示例。某些新属性在不适用或旧版本的 OpenSSL 库中不可用时可能不会显示。

    接收:来自 localhost (localhost [127.0.0.1])
    (使用TLSv1.3,加密套件TLS_AES_256_GCM_SHA384 (256/256位)
    密钥交换X25519,服务器签名RSA-PSS (2048位),服务器摘要SHA256)
    (未请求客户端证书)
    接收自本地主机 (本地主机 [127.0.0.1])
    (使用TLSv1.3,加密套件TLS_AES_256_GCM_SHA384 (256/256位)
    密钥交换X25519,服务器签名RSA-PSS (2048位)服务器摘要SHA256
    客户端签名ECDSA (P-256)客户端摘要SHA256)
    (客户端 CN "example.org", 签发者 "example.org" (未验证))
    
    • "密钥交换"属性记录用于密钥协商的"Diffie-Hellman"组类型。可能的值包括"DHE"、"ECDHE"、"X25519"和"X448"。使用"DHE"时,素数的位数将在算法名称后用括号报告;使用"ECDHE"时,将报告曲线名称。
    • "server-signature"属性显示服务器使用的公钥签名算法。对于"RSA-PSS",模数位数将以括号形式报告。对于"ECDSA",报告曲线名称。例如,如果服务器同时拥有RSA和ECDSA私钥及证书,即可追踪特定连接使用了哪种签名算法。
    • 新的 "server-digest" 属性记录服务器用于准备握手消息签名所使用的摘要算法。Ed25519 和 Ed448 签名算法不使用此类摘要,因此对于这些签名算法不会显示 "server-digest"。
    • 当客户端证书通过 "smtpd_tls_ask_ccert" 请求且客户端使用 TLS 客户端证书时,"client-signature" 和 "client-digest" 属性将记录客户端 TLS 握手签名的对应属性。

接下来将解释预期收到的cipher-namekey-size以及对等验证状态信息。

哪些密码套件支持前向保密? ;

支持前向保密性的密码套件有数十种。以下是 OpenSSL 1.0.1e 中可用密码套件列表的开头部分。该列表按 Postfix 的默认优先级排序。它排除了仅用于身份验证而不加密的空密码套件,以及加密强度过低而无法提供实质性保密性的出口级和低级密码套件。第一列显示密码名称,第二列显示密钥交换方法。

$ openssl ciphers -v \
'aNULL:-aNULL:kEECDH:kEDH:+RC4:!eNULL:!EXPORT:!LOW:@STRENGTH' | 
awk '{printf "%-32s %s\n", $1, $3}'
AECDH-AES256-SHA Kx=ECDH
ECDHE-RSA-AES256-GCM-SHA384 Kx=ECDH
ECDHE-ECDSA-AES256-GCM-SHA384 Kx=ECDH
ECDHE-RSA-AES256-SHA384 Kx=ECDH
ECDHE-ECDSA-AES256-SHA384 Kx=ECDH
ECDHE-RSA-AES256-SHA Kx=ECDH
ECDHE-ECDSA-AES256-SHA Kx=ECDH
ADH-AES256-GCM-SHA384 Kx=DH
ADH-AES256-SHA256 Kx=DH
ADH-AES256-SHA Kx=DH
ADH-CAMELLIA256-SHA Kx=DH
DHE-DSS-AES256-GCM-SHA384 Kx=DH
DHE-RSA-AES256-GCM-SHA384 Kx=DH
DHE-RSA-AES256-SHA256 Kx=DH
...

截至目前,所有支持前向保密性的加密算法在 OpenSSL 名称的第一部分中具有以下五个值之一:"AECDH"、"ECDHE"、"ADH"、"EDH" 或 "DHE"。不支持前向保密性的加密算法名称不以这些前缀开头。这种模式很可能持续存在,直到出现某种新的密钥交换机制,该机制同时支持前向保密性。

非出口加密算法的实际密钥长度与原始算法密钥长度通常一致,但对于遗留出口加密算法,实际密钥可能因人工缩短而不同。

从 TLS 1.3 开始,密码名称不再包含足够的信息来确定使用的向前保密方案,但 TLS 1.3 始终使用向前保密。在客户端,最新版本的 Postfix 会为 TLS 1.3 连接记录额外信息,报告签名和密钥交换算法。以下是两个示例(长单行消息已折行以提高可读性):

postfix/smtp[process-id]:
与 127.0.0.1[127.0.0.1]:25 建立未受信任的 TLS 连接:
TLSv1.3,使用密码套件 TLS_AES_256_GCM_SHA384(256/256 位)
密钥交换 X25519服务器签名 RSA-PSS (2048 位)服务器摘要 SHA256
客户端签名 ECDSA (P-256)客户端摘要 SHA256
postfix/smtp[进程 ID]:
与 127.0.0.1[127.0.0.1]:25 建立了不受信任的 TLS 连接:
TLSv1.3,使用加密套件 TLS_AES_256_GCM_SHA384(256/256 位)
密钥交换 ECDHE (P-256) 服务器签名 ECDSA (P-256) 服务器摘要 SHA256

在上述连接中,"key-exchange" 值记录了用于密钥协商的 "Diffie-Hellman" 算法。 "server-signature" 值记录了服务器用于签名密钥交换的公钥算法。 "server-digest" 值记录了用于准备签名数据的哈希算法。对于 "ED25519" 和 "ED448",无需单独的哈希算法。

Postfix SMTP 服务器日志示例:

postfix/smtpd[进程 ID]:
与本地主机[127.0.0.1]:25 建立的未受信任的 TLS 连接:
TLSv1.3 使用密码套件 TLS_AES_256_GCM_SHA384(256/256 位)
密钥交换 X25519 服务器签名 RSA-PSS(2048 位)服务器摘要 SHA256
客户端签名 ECDSA(P-256)客户端摘要 SHA256
postfix/smtpd[进程ID]:
与本地主机[127.0.0.1]建立匿名TLS连接:
TLSv1.3,使用加密套件TLS_AES_256_GCM_SHA384(256/256位)
服务器签名:RSA-PSS(2048位)服务器摘要:SHA256
postfix/smtpd[进程ID]:
匿名TLS连接已从本地主机[127.0.0.1]建立:
TLSv1.3,使用加密套件TLS_AES_256_GCM_SHA384(256/256位)
服务器签名:ED25519

注意:Postfix ≥ 3.4 服务器日志可能还包含一个 "to sni-name" 元素,用于记录连接中使用的替代服务器证书链。这发生在客户端使用 TLS SNI 扩展时,服务器根据客户端的 SNI 值选择非默认证书链:

postfix/smtpd[process-id]:
与客户端.example[192.0.2.1]建立的未受信任的 TLS 连接
到服务器.example:TLSv1.3,使用密码套件 TLS_AES_256_GCM_SHA384(256/256 位)
密钥交换 X25519,服务器签名 RSA-PSS(2048 位),服务器摘要 SHA256
客户端签名 ECDSA (P-256) 客户端摘要 SHA256

Postfix 日志中的 "Anonymous"、"Untrusted" 等是什么意思? ;

以下验证级别在不同程度上易受中间人攻击。若此类攻击构成威胁,则 SMTP 客户端需以足够安全的方式验证远程 SMTP 服务器。例如通过公钥或证书的指纹(CA 或叶证书)。请注意,传统 PKI 依赖多个可信实体,易被国家资助的攻击者破坏。

匿名(无对等证书)

Postfix SMTP 客户端:在使用机会性 TLS("可选"安全级别)时,Postfix SMTP 客户端不会验证对等证书中的任何信息。在此情况下,它启用并优先使用匿名密码套件,其中远程 SMTP 服务器不提供证书(这些密码套件必然提供前向保密性)。当远程 SMTP 服务器也支持匿名 TLS 且同意此类密码套件时,验证状态将被记录为"匿名"。

Postfix SMTP 服务器:这是最常见的情况,因为客户端证书是可选的,且 Postfix SMTP 服务器默认不请求客户端证书(参见 smtpd_tls_ask_ccert)。即使请求了客户端证书,远程 SMTP 客户端也可能不会发送证书。与 Postfix SMTP 客户端不同,Postfix SMTP 服务器的"匿名"验证状态并不意味着密码套件是匿名的,这对应于 服务器 未发送证书。

未受信任(同行证书未由受信任的 CA 签名)

Postfix SMTP 客户端: 远程 SMTP 服务器提供了证书,但 Postfix SMTP 客户端无法验证签发 CA 的签名。在支持机会式 TLS 的情况下,这在不支持匿名密码套件的远程 SMTP 服务器上很常见。

Postfix SMTP 服务器: 远程 SMTP 客户端提供了证书,但 Postfix SMTP 服务器无法验证签发 CA 的签名。这可能发生在服务器配置为请求客户端证书时(参见 smtpd_tls_ask_ccert)。

受信任的(由受信任的 CA 签名的对等证书,未验证的对等名称)

Postfix SMTP 客户端: 远程 SMTP 服务器的证书由 Postfix SMTP 客户端信任的 CA 签名,但客户端未配置为将目标服务器名称与证书进行验证,或服务器证书中不包含任何匹配的名称。这在机会主义 TLS 中很常见(smtp_tls_security_level 为 "may",或在其他情况下为 "dane" 且无可用 TLSA DNS 记录)时,当 Postfix SMTP 客户端的受信任 CA 可验证远程 SMTP 服务器证书的真实性,但客户端未配置或无法验证服务器名称。

Postfix SMTP 服务器: 远程 SMTP 客户端证书由 Postfix SMTP 服务器信任的 CA 签名。Postfix SMTP 服务器从未将远程 SMTP 客户端名称与客户端证书中的名称进行验证。由于客户端选择连接到服务器,Postfix SMTP 服务器对特定客户端主机名没有预期。

已验证(同行证书由受信任的 CA 签名且同行名称已验证;或:同行证书包含预期公钥或证书指纹)

Postfix SMTP 客户端: 远程 SMTP 服务器的证书由 Postfix SMTP 客户端信任的 CA 签名,且证书名称与目标或服务器名称匹配。Postfix SMTP 客户端已配置为要求验证名称,否则验证状态将仅为"受信任"。

Postfix SMTP 客户端: "已验证"状态也可能表示 Postfix SMTP 客户端成功将预期指纹与远程 SMTP 服务器公钥或证书匹配。预期指纹可能来自smtp_tls_policy_maps或TLSA(安全)DNS记录。Postfix SMTP客户端忽略CA签名。

Postfix SMTP 服务器:状态永远不会是"已验证",因为 Postfix SMTP 服务器从未将远程 SMTP 客户端名称与客户端证书中的名称进行验证,并且 Postfix SMTP 服务器不期望在客户端公钥或证书中找到特定的指纹。

致谢 ;

  • Postfix 的 TLS 支持最初由 Lutz Jänicke 在 Cottbus 技术大学开发。
  • Wietse Venema 采用了代码并进行了重构。
  • Viktor Dukhovni 实现了对许多后续 TLS 功能的支持,包括 EECDH,并撰写了本文件的初始版本。