Appendix G. Local Policy Considerations (本地策略考虑)
Appendix G. Local Policy Considerations (本地策略考虑)
本附录讨论接收方在实施 SPF 时应考虑的本地策略。
G.1 Policy for SPF Pass (SPF Pass 的策略)
G.1.1 基本处理
SPF Pass 结果: 发件人已通过 SPF 验证
建议操作:
1. 接受邮件
2. 继续其他反垃圾邮件检查
3. 不应仅基于 SPF pass 就完全信任
原因:
- SPF 仅验证 SMTP 发件人
- 不验证消息内容
- 不验证 From: 标头
- 攻击者可以从授权服务器发送垃圾邮件
G.1.2 评分系统
SpamAssassin 示例:
# SPF pass 降低垃圾邮件分数
score SPF_PASS -0.001
# 但不足以完全信任
# 仍需要其他检查: 内容分析, DKIM, DMARC 等
rspamd 示例:
-- SPF pass 给予正面权重
spf {
symbol_pass = "R_SPF_ALLOW";
symbol_fail = "R_SPF_FAIL";
symbol_softfail = "R_SPF_SOFTFAIL";
symbol_neutral = "R_SPF_NEUTRAL";
-- 权重
score = {
R_SPF_ALLOW = -0.2,
R_SPF_FAIL = 6.0,
R_SPF_SOFTFAIL = 3.0,
R_SPF_NEUTRAL = 0.0
}
}
G.1.3 白名单考虑
已知良好发件人:
# 对于已知可信域的 SPF pass, 可以降低其他检查的严格度
if spf_pass and sender_domain in trusted_domains:
reduce_spam_score()
skip_greylisting()
示例实现:
TRUSTED_DOMAINS = [
'example.com',
'partner.org',
'bank.com'
]
def apply_whitelist_policy(spf_result, sender_domain):
if spf_result == 'pass' and sender_domain in TRUSTED_DOMAINS:
return 'WHITELIST'
return 'CONTINUE_CHECKS'
G.2 Policy for SPF Fail (SPF Fail 的策略)
G.2.1 严格策略
立即拒绝:
# Postfix 配置
smtpd_recipient_restrictions =
reject_unauth_destination,
check_policy_service unix:private/spf-policy,
permit
# SPF 策略服务
if spf_result == 'fail':
return 'REJECT SPF validation failed'
SMTP 响应示例:
550 5.7.1 SPF validation failed for [email protected]
550 5.7.1 Please see http://www.openspf.org/Why?s=mfrom&id=sender%40example.com&ip=192.0.2.1
优点:
- 有效阻止伪造邮件
- 减少垃圾邮件
缺点:
- 可能阻止合法的转发邮件
- 可能阻止邮件列表邮件
- 配置错误的 SPF 记录导致误报
G.2.2 宽松策略
增加垃圾邮件分数:
# SpamAssassin
score SPF_FAIL 5.0
score SPF_SOFTFAIL 3.0
# 不自动拒绝, 而是增加分数
# 结合其他因素决定
实现示例:
def calculate_spam_score(message):
score = 0
# SPF 检查
if message.spf_result == 'fail':
score += 5.0
elif message.spf_result == 'softfail':
score += 3.0
elif message.spf_result == 'pass':
score -= 0.2
# 其他检查...
if message.dkim_result == 'pass':
score -= 0.5
# 内容分析
score += content_analysis(message)
# 决策
if score > 10:
return 'REJECT'
elif score > 5:
return 'QUARANTINE'
else:
return 'ACCEPT'
G.2.3 渐进式策略
阶段 1: 监控模式(0-30天)
# 记录但不采取行动
if spf_result == 'fail':
log_spf_failure(sender, recipient, ip)
add_header('X-SPF-Status', 'fail')
# 但仍然接受邮件
阶段 2: 标记模式(31-90天)
# 标记并隔离
if spf_result == 'fail':
move_to_spam_folder()
add_header('X-SPF-Warning', 'This message failed SPF validation')
阶段 3: 拒绝模式(90天后)
# 拒绝明显的伪造
if spf_result == 'fail' and not is_exception(sender):
reject('SPF validation failed')
G.2.4 例外列表
场景: 已知会导致 SPF fail 的合法来源
EXCEPTIONS = {
# 邮件列表
'List-Id': [
'important-list.mailinglist.org',
'company-announce.lists.example.com'
],
# 转发服务
'forward_domains': [
'forward.alumni.university.edu'
],
# 特定发件人
'sender_whitelist': [
'[email protected]'
]
}
def should_apply_spf_fail_policy(message):
# 检查是否为邮件列表
list_id = message.get_header('List-Id')
if list_id and list_id in EXCEPTIONS['List-Id']:
return False
# 检查发件人白名单
sender = message.get_sender()
if sender in EXCEPTIONS['sender_whitelist']:
return False
return True
G.3 Policy for SPF Permerror (SPF Permerror 的策略)
G.3.1 处理方式
Permerror 含义: SPF 记录存在永久性错误
可能原因:
- 语法错误
- 多个 SPF 记录
- DNS 查询限制超标
- 无效的机制或修饰符
建议策略:
if spf_result == 'permerror':
# 记录错误
log_spf_permerror(sender_domain, error_details)
# 通知管理员(可选)
notify_admin(sender_domain, error_details)
# 策略选项:
# 选项 1: 视为无 SPF(宽松)
treat_as_none()
# 选项 2: 增加垃圾邮件分数(推荐)
add_spam_score(2.0)
# 选项 3: 拒绝(严格, 不推荐)
# reject('Sender has invalid SPF configuration')
G.3.2 通知机制
通知发送域管理员:
def notify_sender_domain(domain, error):
# 尝试查找 postmaster 地址
postmaster = f"postmaster@{domain}"
# 发送通知(限制频率, 避免骚扰)
if not recently_notified(domain):
send_notification(
to=postmaster,
subject=f"SPF configuration error for {domain}",
body=f"""
Your domain's SPF record has an error:
{error}
Please correct this issue to improve email deliverability.
"""
)
mark_as_notified(domain)
G.4 Policy for SPF Temperror (SPF Temperror 的策略)
G.4.1 处理方式
Temperror 含义: 临时性 DNS 错误
建议策略:
if spf_result == 'temperror':
# 选项 1: 临时拒绝(推荐)
return '4xx Temporary DNS error, please try again later'
# 选项 2: 接受但标记
add_header('X-SPF-Temperror', 'true')
log_temperror(sender, recipient)
accept_message()
SMTP 响应:
450 4.7.1 SPF temporary error during validation, please retry
优点: 发件人 MTA 会稍后重试
G.4.2 重试逻辑
Postfix 配置:
# main.cf
# 临时失败时的重试延迟
queue_run_delay = 300s
minimal_backoff_time = 300s
maximal_backoff_time = 4000s
监控 Temperror:
# 如果 temperror 率过高, 可能是 DNS 问题
def monitor_temperror_rate():
rate = get_temperror_rate()
if rate > 0.05: # 5%
alert("High SPF temperror rate detected: DNS issues?")
G.5 Policy for SPF Softfail (SPF Softfail 的策略)
G.5.1 处理建议
Softfail 含义: 主机可能未授权(~all)
推荐策略:
- 接受邮件
- 标记为可疑
- 增加适度的垃圾邮件分数
- 可选: 放入垃圾邮件文件夹
实现:
if spf_result == 'softfail':
# 增加垃圾邮件分数(比 fail 低)
spam_score += 2.0
# 添加标头
add_header('X-SPF-Status', 'softfail')
# 如果总分数超过阈值, 隔离
if spam_score > 5.0:
move_to_spam_folder()
else:
deliver_to_inbox()
G.6 Policy for SPF Neutral and None (SPF Neutral 和 None 的策略)
G.6.1 Neutral (?all)
含义: 发件人明确表示不断言
策略:
if spf_result == 'neutral':
# 视为无 SPF
# 不增加或减少垃圾邮件分数
# 继续其他检查
pass
G.6.2 None (无 SPF 记录)
含义: 域没有发布 SPF 记录
策略选项:
选项 1: 宽松(推荐)
if spf_result == 'none':
# 不惩罚无 SPF 的域
# 许多合法域仍未部署 SPF
continue_normal_checks()
选项 2: 轻微惩罚
if spf_result == 'none':
# 轻微增加分数(鼓励 SPF 部署)
spam_score += 0.5
G.7 Combined Authentication Policies (综合认证策略)
G.7.1 SPF + DKIM
决策矩阵:
| SPF | DKIM | 操作 |
|---|---|---|
| pass | pass | 接受(低垃圾邮件分数) |
| pass | fail | 接受(正常检查) |
| fail | pass | 接受(DKIM 更可靠) |
| fail | fail | 增加分数或拒绝 |
实现:
def evaluate_authentication(spf, dkim):
if spf == 'pass' and dkim == 'pass':
return -1.0 # 降低垃圾邮件分数
elif spf == 'pass' or dkim == 'pass':
return 0.0 # 中性
elif spf == 'fail' and dkim == 'fail':
return 8.0 # 高度可疑
else:
return 2.0 # 轻微可疑
G.7.2 SPF + DKIM + DMARC
DMARC 对齐检查:
def check_dmarc_alignment(message):
# DMARC 要求 SPF 或 DKIM 与 From: 域对齐
from_domain = extract_domain(message.get_header('From'))
# SPF 对齐
spf_aligned = (message.spf_result == 'pass' and
message.mail_from_domain == from_domain)
# DKIM 对齐
dkim_aligned = (message.dkim_result == 'pass' and
message.dkim_domain == from_domain)
# DMARC 通过需要至少一个对齐
dmarc_pass = spf_aligned or dkim_aligned
return dmarc_pass
策略应用:
def apply_dmarc_policy(message):
dmarc_policy = lookup_dmarc_policy(message.from_domain)
if not dmarc_policy:
# 无 DMARC 策略
return apply_local_policy(message)
dmarc_pass = check_dmarc_alignment(message)
if dmarc_pass:
return 'ACCEPT'
# DMARC 失败, 应用发布的策略
if dmarc_policy.p == 'reject':
return 'REJECT'
elif dmarc_policy.p == 'quarantine':
return 'QUARANTINE'
else: # none
return apply_local_policy(message)
G.8 User-Level Policies (用户级策略)
G.8.1 用户可配置选项
白名单:
用户可以添加信任的发件人:
- 即使 SPF fail 也接受
- 绕过垃圾邮件检查
黑名单:
用户可以阻止特定发件人:
- 即使 SPF pass 也拒绝
严格程度:
用户可以选择:
- 宽松: 接受所有邮件, 仅标记可疑
- 正常: 平衡安全和可用性
- 严格: 积极过滤, 可能误报
G.8.2 实现示例
class UserPolicy:
def __init__(self, user_id):
self.user_id = user_id
self.whitelist = load_whitelist(user_id)
self.blacklist = load_blacklist(user_id)
self.strictness = load_strictness(user_id)
def evaluate(self, message):
sender = message.get_sender()
# 检查黑名单
if sender in self.blacklist:
return 'REJECT'
# 检查白名单
if sender in self.whitelist:
return 'ACCEPT'
# 应用基于严格程度的策略
if self.strictness == 'strict':
return self._strict_policy(message)
elif self.strictness == 'normal':
return self._normal_policy(message)
else: # loose
return self._loose_policy(message)
def _strict_policy(self, message):
if message.spf_result == 'fail':
return 'REJECT'
elif message.spam_score > 5.0:
return 'REJECT'
return 'ACCEPT'
def _normal_policy(self, message):
if message.spam_score > 10.0:
return 'REJECT'
elif message.spam_score > 5.0:
return 'QUARANTINE'
return 'ACCEPT'
def _loose_policy(self, message):
if message.spam_score > 15.0:
return 'QUARANTINE'
return 'ACCEPT'
G.9 Reporting and Feedback (报告和反馈)
G.9.1 DMARC 报告
生成聚合报告:
def generate_dmarc_report(domain, period_start, period_end):
"""
生成 DMARC 聚合报告 (RUA)
"""
records = get_dmarc_records(domain, period_start, period_end)
report = {
'report_metadata': {
'org_name': 'Your Organization',
'email': '[email protected]',
'report_id': generate_report_id(),
'date_range': {
'begin': period_start,
'end': period_end
}
},
'policy_published': lookup_dmarc_policy(domain),
'records': []
}
# 按 IP 聚合
for ip, data in aggregate_by_ip(records):
report['records'].append({
'source_ip': ip,
'count': data['count'],
'policy_evaluated': {
'disposition': data['disposition'],
'dkim': data['dkim_result'],
'spf': data['spf_result']
}
})
return report
G.9.2 反馈循环
处理用户反馈:
def handle_user_feedback(message_id, feedback_type):
"""
处理用户反馈 (报告垃圾邮件/非垃圾邮件)
"""
message = retrieve_message(message_id)
if feedback_type == 'spam':
# 用户报告为垃圾邮件
update_reputation(message.sender, -1.0)
# 如果是 SPF pass 但被报告为垃圾邮件
if message.spf_result == 'pass':
log_false_positive(message.sender_domain)
elif feedback_type == 'not_spam':
# 用户报告误判
update_reputation(message.sender, +1.0)
# 如果是 SPF fail 但是合法邮件
if message.spf_result == 'fail':
consider_exception(message.sender)
G.10 Best Practices Summary (最佳实践摘要)
G.10.1 推荐策略
1. 结合多种认证方法(SPF + DKIM + DMARC)
2. 不要仅依赖 SPF 做决策
3. 使用渐进式部署(监控 → 标记 → 拒绝)
4. 维护例外列表(邮件列表、已知转发等)
5. 提供用户可配置选项
6. 实施报告和反馈机制
7. 定期审查和调整策略
8. 监控误报率
9. 保持策略文档更新
10. 与发件人社区沟通
G.10.2 避免的陷阱
❌ 仅基于 SPF fail 就拒绝所有邮件
❌ 忽略 DKIM 结果
❌ 没有例外机制
❌ 不监控误报
❌ 策略过于严格导致合法邮件丢失
❌ 策略过于宽松失去防护作用
❌ 不提供用户反馈渠道
❌ 不生成 DMARC 报告
G.10.3 持续改进
1. 定期审查日志和报告
2. 分析误报和漏报
3. 调整评分权重
4. 更新白名单/黑名单
5. 跟踪行业最佳实践
6. 测试新的过滤技术
7. 收集用户反馈
8. 与发件人合作改进