Appendix D. SPF/Mediator Interactions (SPF/中介交互)
Appendix D. SPF/Mediator Interactions (SPF/中介交互)
本附录讨论 SPF 与邮件中介(如邮件列表和转发服务)之间的交互及其影响。
D.1 Originating ADMDs (发起 ADMD)
发起 ADMD 是最初发送邮件的管理域。在存在中介的情况下, 发起 ADMD 需要考虑其邮件可能经过的转发和列表服务。
D.1.1 问题识别
SPF 的基本假设:
- 邮件直接从授权服务器发送到接收方
- MAIL FROM 标识保持不变
- 发送 IP 地址在 SPF 记录中授权
中介打破的假设:
- 邮件经过中间系统
- 可能从不同的 IP 地址重新发送
- MAIL FROM 可能被修改或保持原样
D.1.2 发起方的选择
选项 1: 使用宽松的 SPF 策略
example.com IN TXT "v=spf1 mx ~all"
优点:
- 允许合法转发
- 减少误报
缺点:
- 削弱了 SPF 的保护
- 容易被伪造
选项 2: 依赖其他认证方法
发布 DKIM 签名:
- DKIM 在转发时仍然有效
- 不依赖发送 IP 地址
- 与 DMARC 配合使用
选项 3: 接受 SPF 失败
example.com IN TXT "v=spf1 mx -all"
- 接受某些合法邮件可能被标记或拒绝
- 依赖用户使用正确的发送方法
- 建议用户不要转发, 而是使用"发送副本"功能
D.1.3 最佳实践
建议配置:
1. 发布严格的 SPF 记录(-all)
2. 同时实施 DKIM 签名
3. 发布 DMARC 策略
4. 教育用户关于转发的问题
DMARC 配置示例:
_dmarc.example.com IN TXT "v=DMARC1; p=quarantine; sp=quarantine; adkim=r; aspf=r; pct=100; rua=mailto:[email protected]"
参数说明:
adkim=r: DKIM 宽松对齐aspf=r: SPF 宽松对齐p=quarantine: 失败时隔离邮件
D.2 Mediators (中介)
中介是在邮件传输链中修改或重新发送邮件的系统。常见的中介包括邮件列表、转发服务和自动回复系统。
D.2.1 邮件列表
问题:
传统邮件列表保留原始 MAIL FROM:
原始: MAIL FROM:<[email protected]>
经过列表: MAIL FROM:<[email protected]>
发送 IP: list-server.mailinglist.org
SPF 检查:
查询 example.com 的 SPF 记录
检查 list-server.mailinglist.org 的 IP
结果: fail(IP 不在 example.com 的 SPF 记录中)
D.2.2 解决方案
方案 1: 重写 MAIL FROM(推荐)
重写后: MAIL FROM:<[email protected]>
Reply-To: [email protected]
优点:
- SPF 检查通过
- 退信返回到列表服务器
- 接收方可以正确识别
缺点:
- 改变了原始发件人信息
- 用户需要适应 Reply-To
实现示例(Mailman):
# Mailman 配置
from_is_list = 2 # Munge From, add Reply-To
方案 2: 使用 SRS (Sender Rewriting Scheme)
SRS 是一种更复杂的重写方案:
原始: [email protected]
重写为: [email protected]
SRS 格式:
SRS0=<hash>=<timestamp>=<domain>=<localpart>@<forwarder>
示例:
[email protected]
优点:
- 保留原始发件人信息(在 local-part 中)
- SPF 检查通过
- 可以追踪和验证
缺点:
- 实现复杂
- 需要 SRS 数据库
- local-part 变得难以阅读
方案 3: 使用 DKIM 而不依赖 SPF
配置:
1. 原始发件人使用 DKIM 签名
2. 列表服务器保留原始签名
3. 列表服务器添加自己的 DKIM 签名
4. 接收方验证 DKIM(忽略 SPF 失败)
DMARC 配置:
_dmarc.example.com IN TXT "v=DMARC1; p=none; adkim=r; aspf=r"
p=none: 不强制执行(允许 SPF 失败)adkim=r: DKIM 宽松对齐(允许列表签名)
D.2.3 邮件转发服务
类型 1: 简单转发
用户配置: [email protected] → [email protected]
转发服务保持: MAIL FROM:<[email protected]>
从转发服务器的 IP 发送
SPF 问题:
Gmail 检查 original.com 的 SPF
转发服务器的 IP 不在 SPF 记录中
结果: fail
解决方案 A: SRS 重写
转发服务使用 SRS:
MAIL FROM:<[email protected]>
解决方案 B: 使用 ~all 而不是 -all
original.com IN TXT "v=spf1 mx ~all"
转发的邮件会得到 softfail 而不是 fail, 更可能被接受。
类型 2: .forward 文件
# ~/.forward
[email protected]
问题同简单转发。
最佳实践:
1. 转发服务应该实施 SRS
2. 原始发件人应该使用 DKIM
3. 接收方应该考虑 DKIM 结果, 不仅仅依赖 SPF
D.2.4 自动回复和通知系统
问题:
原始邮件: [email protected] → [email protected]
自动回复: MAIL FROM:<[email protected]>
(从 company.com 的服务器发送)
SPF 检查会失败。
解决方案:
使用空 MAIL FROM 发送通知:
MAIL FROM:<>
From: Mail Delivery System <[email protected]>
或者:
使用本地地址:
MAIL FROM:<[email protected]>
From: Automatic Reply <[email protected]>
Reply-To: [email protected]
D.3 Receiving ADMDs (接收 ADMD)
接收 ADMD 需要处理来自中介的邮件, 并做出适当的决策。
D.3.1 识别中介邮件
标识符:
1. Received 标头中的中间跳数
2. List-* 标头(邮件列表)
3. Precedence: bulk 或 list
4. SRS 格式的 MAIL FROM
5. DKIM 签名来自已知列表服务
示例检测逻辑:
def is_mailing_list(message):
# 检查 List-* 标头
list_headers = [
'List-Id', 'List-Post', 'List-Unsubscribe',
'List-Help', 'List-Subscribe', 'List-Owner'
]
for header in list_headers:
if message.get(header):
return True
# 检查 Precedence
if message.get('Precedence') in ['bulk', 'list']:
return True
# 检查 SRS
mail_from = message.get('Return-Path', '')
if mail_from.startswith('SRS0=') or mail_from.startswith('SRS1='):
return True
return False
D.3.2 策略建议
策略 1: 宽松对待列表邮件
if is_mailing_list(message):
if spf_result == 'fail':
# 检查 DKIM
if dkim_result == 'pass':
accept_message()
else:
# 检查列表声誉
if list_is_trusted(list_id):
accept_message()
else:
quarantine_message()
策略 2: 依赖 DMARC
if dmarc_result == 'pass':
accept_message()
elif spf_result == 'fail' and dkim_result == 'fail':
reject_message()
else:
# SPF 或 DKIM 之一通过
if is_mailing_list(message):
accept_message()
else:
apply_content_filtering()
策略 3: 用户特定规则
用户可以配置白名单:
- 信任的邮件列表
- 已知的转发地址
- 特定域的宽松处理
D.3.3 实现建议
SpamAssassin 配置:
# 对邮件列表宽松处理
header LIST_ID exists:List-Id
score LIST_ID -0.1
# SPF fail 但有 List-Id 标头
meta SPF_FAIL_LIST (SPF_FAIL && LIST_ID)
score SPF_FAIL_LIST 0.5
# 正常 SPF fail(无列表标头)
score SPF_FAIL 5.0
Postfix 配置:
# smtpd_recipient_restrictions
reject_unauth_destination,
check_policy_service unix:private/spf-policy,
permit
# SPF 策略服务可以识别列表邮件并相应调整
rspamd 配置:
-- SPF 模块配置
spf {
-- 对邮件列表应用不同的权重
symbol_fail = "R_SPF_FAIL";
symbol_softfail = "R_SPF_SOFTFAIL";
-- 自定义规则
custom_symbols = {
R_SPF_FAIL_LIST = {
score = 2.0,
description = "SPF failed but from mailing list",
condition = function(task)
local spf = task:get_symbol('R_SPF_FAIL')
local list_id = task:get_header('List-Id')
return spf and list_id
end
}
}
}
D.4 [互操作性建议]
D.4.1 标准化
使用标准标头:
邮件列表应该添加:
- List-Id: <list-name.domain.com>
- List-Post: <mailto:[email protected]>
- List-Unsubscribe: <mailto:[email protected]>
- Precedence: bulk
DKIM 签名:
列表服务器应该:
1. 保留原始 DKIM 签名(如果有)
2. 添加自己的 DKIM 签名
3. 不修改已签名的标头
D.4.2 通信
发件人通知:
列表服务应该通知订阅者:
- 邮件将从列表服务器重新发送
- MAIL FROM 将被重写
- 建议使用 Reply-To 回复列表
接收方文档:
记录接收方策略:
- 如何处理列表邮件
- 白名单机制
- 用户可配置的选项
D.5 [未来发展方向]
D.5.1 ARC (Authenticated Received Chain)
RFC 8617 引入了 ARC:
ARC 允许中介添加认证信息:
- ARC-Seal: 中介的签名
- ARC-Message-Signature: 消息签名
- ARC-Authentication-Results: 认证结果
示例:
ARC-Seal: i=1; a=rsa-sha256; t=1234567890; cv=none;
d=mailinglist.org; s=arc-seal; b=...
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed;
d=mailinglist.org; s=arc-msg; h=from:to:subject; b=...
ARC-Authentication-Results: i=1; mailinglist.org;
spf=pass [email protected];
dkim=pass header.d=example.com
优势:
- 保留原始认证结果
- 允许链式验证
- 接收方可以信任中介
D.5.2 建议
对于新部署:
- 实施 DKIM 和 SPF
- 发布 DMARC 策略
- 考虑支持 ARC(如果是中介)
- 测试与常见邮件列表的互操作性
对于现有系统:
- 审查 SPF 策略的严格程度
- 监控 DMARC 报告
- 识别问题的来源(列表、转发等)
- 调整策略或实施 SRS