6. 问题检测和处理 (Problem Detection and Handling)
本节说明 SMTP 实现应如何处理邮件传输期间出现的各种问题.
6.1. 可靠投递和通过电子邮件的响应 (Reliable Delivery and Replies by Email)
投递状态通知 (Delivery Status Notifications, DSN)
当邮件无法投递时, 发送系统应生成投递状态通知 (bounce message), 通知发送者.
DSN 要求:
- 必须发送到 reverse-path (MAIL FROM 地址)
- 必须使用 null reverse-path (
MAIL FROM:<>) 以防止循环 - 应包含原始消息或头部
- 应说明失败原因
Bounce 消息示例:
From: [email protected]
To: [email protected]
Subject: Undelivered Mail Returned to Sender
Your message to [email protected] could not be delivered:
550 5.1.1 Mailbox does not exist
--- Original message headers follow ---
From: [email protected]
To: [email protected]
Subject: Important message
...
成功通知
通常不发送投递成功通知, 因为它们会产生额外流量. 不过, DSN 扩展允许显式请求正向投递确认.
超时和重试
重试计划:
- 初始失败: 30 分钟后重试
- 后续失败: 指数退避 (1 小时, 2 小时, 4 小时等)
- 最大重试期间: 至少 4-5 天
- 最终失败: 向发送者发送 bounce
警告消息:
- 4-6 小时后向发送者发送延迟警告
- 包含延迟原因
- 告知发送者投递尝试仍在继续
6.2. 不想要,未请求和“攻击”消息 (Unwanted, Unsolicited, and "Attack" Messages)
SMTP 服务器应实现防滥用保护:
垃圾邮件防护
技术:
- 接收者验证: 接受邮件前验证接收者存在
- 发送者验证: 检查 SPF, DKIM, DMARC
- 速率限制: 限制每连接/每小时消息数
- Greylisting: 临时拒绝首次出现的发送者
- 内容过滤: 扫描消息内容中的垃圾邮件模式
- 信誉系统: 跟踪发送者信誉
拒绝服务 (DoS) 保护
防御措施:
- 连接限制: 限制单个 IP 的并发连接数
- 速率限制: 限制每秒命令数
- Tarpit: 放慢可疑发送者
- 资源限制: 限制每连接 CPU 和内存使用
- 超时强制: 断开空闲连接
目录收集攻击 (Directory Harvest Attacks, DHA)
攻击者发送带有猜测地址的 RCPT TO 命令, 以查找有效邮箱.
对策:
Vulnerable:
C: RCPT TO:<[email protected]>
S: 550 No such user (reveals invalid)
C: RCPT TO:<[email protected]>
S: 250 Ok (reveals valid)
Protected:
- 延迟对 RCPT 命令的响应
- 对有效和无效地址使用相同响应 (例如总是 "250 Ok")
- 仅在 DATA 期间或之后验证接收者
- 对 RCPT 命令进行速率限制
Backscatter 防止
Backscatter 是指发送到伪造发送者地址的 bounce, 会产生附带垃圾邮件.
防止方式:
- 在 SMTP 时拒绝: 在 SMTP 事务期间拒绝无效接收者 (DATA 之前)
- 验证发送者: 不接受来自伪造发送者的邮件
- 不生成 Bounce: 对来自未知/可疑来源的邮件不生成 bounce
Good Practice:
C: RCPT TO:<[email protected]>
S: 550 5.1.1 User unknown (reject immediately)
Bad Practice:
C: RCPT TO:<[email protected]>
S: 250 Ok (accept mail)
(later generate bounce to forged sender)
6.3. 循环检测 (Loop Detection)
邮件循环发生在消息反复通过相同服务器转发时.
检测方法
方法 1: Received 头部计数
Count Received headers in message
If count > 100:
Reject with "554 5.4.6 Too many hops"
方法 2: Received 头部分析
Parse Received headers
Check if current server appears multiple times
If yes:
Detect loop and reject
方法 3: Message-ID 跟踪
Remember Message-ID of recently processed messages
If same Message-ID seen again within short time:
Likely a loop, reject
循环防止
最佳实践:
- 限制 Hop: 超过 50-100 个 Received 头部后拒绝
- 检测模式: 识别 trace 中重复的服务器名称
- 打破循环: 不转发看起来正在循环的邮件
- 配置审查: 避免创建循环的转发规则
循环示例:
mail.a.example -> mail.b.example ->
mail.c.example -> mail.a.example (loop!)
6.4. 补偿不规则性 (Compensating for Irregularities)
现实中的 SMTP 实现有时会偏离标准. 健壮实现应优雅处理常见不规则性.
常见不规则性
1. 额外空白
某些实现发送额外空格:
Non-standard:
C: MAIL FROM: <[email protected]> (extra spaces)
Robust handling:
Accept and parse correctly
2. 混合大小写命令
Non-standard:
C: Mail From:<[email protected]>
Robust handling:
Treat as case-insensitive
3. 缺少尖括号
某些旧系统省略地址周围的 <>:
Non-standard:
C: MAIL FROM:[email protected]
Robust handling:
Accept both with and without brackets
4. 只有 CR 或 LF (不是 CRLF)
标准要求 CRLF (\r\n), 但某些实现只发送 LF (\n) 或 CR (\r):
Robust handling:
Accept CR, LF, or CRLF as line terminator
(but always send CRLF)
5. 长行
某些实现发送超过限制的行:
Robust handling:
- Accept lines longer than 512 octets
- Split internally if needed
- Don't break during SMTP transaction
健壮性原则
"发送时保守, 接收时宽容"
- 发送: 严格符合标准
- 接收: 优雅接受轻微偏差
- 记录: 记录非标准行为
- 不要: 悄悄修正并转发, 从而维持互操作性问题
错误恢复
遇到不规则性时:
选项 1: 接受并继续
Log warning
Process message normally
选项 2: 带通知地接受
Log warning
Process message
Send DSN to postmaster about issue
选项 3: 拒绝
Only for severe violations
Provide clear error message
Log for analysis
合规测试
实现者应针对以下对象进行测试:
- 符合标准的服务器: 验证正确操作
- 已知有缺陷的实现: 确保互操作性
- 畸形输入: 测试错误处理
- 边界情况: 边界条件和异常场景