RFC 7103《畸形邮件的安全处理建议》中文导读
诚实披露:本页为 IETF RFC 7103 的中文译介版本。原文由 IETF 发布,依 IETF Trust 条款可自由再制与翻译;本译本由 AI 基于人类权威一手文献(RFC 英文原文)辅助整理生成,非人类原创,亦非逐字全文翻译,仅供学习参考。任何规范性判断以英文原文为准,英文原文见 rfc-editor.org/rfc/rfc7103.txt。
摘要
互联网邮件生态长期奉行「宽进严出」的稳健性原则,但过度宽容带来了一个具体的安全后果:同一封畸形邮件,安全过滤模块与最终 MUA 的解析结果可能不同。攻击者正是利用这一解析分歧来绕过检测——过滤器看到的是无害内容,用户看到的却是恶意内容。
RFC 7103 系统梳理了实践中常见的畸形形态(行终止符、地址语法、头字段计数、MIME 结构、正文行长等),并对每一类给出推荐的处置方式,目标是让链路上所有组件对同一封邮件形成一致的理解。
1. 引言:本文的目的与非目的
文档开篇即纠正一个常见误读:
"Jon Postel's directive is often interpreted to mean that any deviance from a specification is acceptable."(§1.1)
译:Jon Postel 的准则常被解读为「任何偏离规范的做法都是可以接受的」。
作者指出这种解读走得太远。本文的目的是:在畸形邮件已经存在的现实前提下,给出安全的解释方式;非目的是:本文不修改 RFC 5322 / MIME,不为畸形语法背书,也不鼓励生成方产出畸形内容。
4. 内容不变性(Invariant Content)
如果处理链上的某个模块在转交前「顺手修好了」邮件格式,后续的分析模块看到的就已经不是原件,可能因此漏检;同时滥用举报也会失真——只有在报告中包含与生成时完全一致的原始邮件时,滥用举报系统才能有效运作。
推荐处置:链路上各模块应基于相同的原始内容进行分析;确需传递加工结论时,通过追加 Received 字段或带外通道传递,而不要改动对外呈现的邮件内容。
5. 邮件提交代理(MSA)
畸形邮件的源头相当一部分来自执行标准不严的提交环节。推荐处置:MSA 与中继 MTA 应更严格地强制执行 RFC 5322 与 MIME 规范,从入口处减少畸形邮件的产生——这比在收件侧做无穷补救更有效。
6. 行终止符
异常:非合规实现产出仅 LF 或孤立 CR 的行终止;严格按标准解析时,整封邮件会被当成「一行」,头体结构随之崩塌。
"Thus, it will typically be safe and helpful to treat an isolated CR or LF as equivalent to a CRLF when parsing a message."(§6)
译:因此在解析邮件时,把孤立的 CR 或 LF 视作等同于 CRLF,通常既安全又有帮助。
注意适用范围:该建议针对原始 SMTP 数据,不适用于已解码的 MIME 实体内部。
7. 头部异常
7.1 废弃与无效语法的转换
异常:地址字段使用废弃或非法语法,攻击者可借此让过滤器与 MUA 提取出不同的地址。总原则:应当拒收;若无法拒收,则按语义在内部转换为合规形式。原文给出的具体形态与安全解释包括:
| 子节 | 异常形态 | 推荐的安全解释 |
|---|---|---|
| 7.1.1 | 废弃的源路由语法(主机-地址形式) | 只保留真实的目标地址,丢弃源路由部分 |
| 7.1.2 | 地址被多层尖括号包裹 | 归一为单层尖括号 |
| 7.1.3 | 尖括号不平衡(缺左或缺右) | 补齐或去除多余括号后取出地址 |
| 7.1.4 | 圆括号不平衡 | 补全为注释或显示名,并明确提取有效地址 |
| 7.1.5 | 地址列表中逗号位置错误(逗号落在尖括号内) | 解释为两个独立地址 |
| 7.1.6 | 引号不平衡 | 取最合理解释,补齐引号形成「显示名 + 地址」 |
| 7.1.7 | 裸 local-part(只有用户名或只有显示名,无完整地址) | 提交代理应拒绝或补全 @主机名;接收方可为内部评估合成一个有效地址 |
7.2 非头部行
异常:头部区域内出现既非合法头字段、又未以空白开头缩进的行。不同实现对「头/体分界在哪」的判断会出现分歧,这是典型的绕过入口。推荐处置:若不采取拒收策略,则向前搜索空行或下一个合法头字段,把该异常行当作折叠续行并入前一字段。
7.3 异常空格
异常:头字段续行中夹入「仅含空白字符」的行,可能被某些实现误判为头体分隔空行。推荐处置:解析时忽略该行;生成邮件时直接移除。
7.4 头部畸形
异常:字段名与冒号之间插入多余空格(如 MIME-Version : 1.0)。虽然旧语法曾允许,但当前共识是移除冒号前的空白并以修正后的形式发出。
7.5 头字段计数
异常:RFC 5322 对某些字段的出现次数有限制,但实践中鲜有强制执行;重复或缺失字段可被用于欺骗(例如两个 From,过滤器看第一个、MUA 显示第二个)。可选处置:拒收、移除超额实例、合并同类字段,或将超额实例重命名(如加 BAD- 前缀)后保留以便取证。
- 7.5.1 重复字段:安全解释为「拼接后的单一字段值」,地址类字段则解释为逗号分隔的单一地址列表。
- 7.5.2 缺失字段:缺
From、Date等必填字段时,可在内部处理中合成(如用信封发件人、用Received时间),但不加入对外呈现;缺Message-ID则应合成并可加入对外呈现。原文在该节明确写明「Option 2 is recommended for handling this case.」(推荐采用方案二)。 - 7.5.3 Return-Path:出现多个实例时,只采信最顶层的一个,其余忽略。
7.6 缺失或错误的字符集信息
异常:文本类 MIME 实体常常缺失或误标 charset;内容中夹带的空字节(NUL)可用于截断解析、隐藏恶意内容。推荐处置:以启发式方式判定字符集,或直接拒绝处理;对孤立的空字节静默丢弃,对大量空字节则拒绝处理。
7.7 八位数据
异常:在未协商相应扩展的情况下,邮件实体或头字段中出现非 ASCII 的八位数据。推荐处置:实体内容按实际已使用的扩展处理;头字段中的八位数据可转换为 encoded-words,或移除/替换;若出现在地址中,则应排除该地址或按 EAI(邮件地址国际化)体系处理。
8. MIME 异常
- 8.1 缺失 MIME-Version 字段:邮件具备完整 MIME 结构却没有
MIME-Version。推荐按「视同存在该字段」进行分析;同时应注意,这种缺失本身可能是刻意规避检测的信号,值得加强审查。 - 8.2 有缺陷的编码:base64 存在历史上的解码「方言」差异,同一段编码在不同解码器下可得到不同结果,可被构造利用。推荐安全模块枚举已知解码方言的可能输出,预判 MUA 侧会看到什么。
9. 正文异常
9.1 超长行:单行超过 998 字符(不含终止符)。部分组件会截断后再分析,超出部分即成为藏匿恶意内容的空间。推荐处置:拒收;或在不改变语义的位置断行;或用 MIME 编码重写,使其在传输中断行、解码后复原。
10. 安全考量
本文档的正文本身即为一份安全考量清单——其中部分攻击面此前未被既有规范覆盖。作者强调,这些实践的目的在于缓解已经存在的风险,而非引入新的风险;实现者在采纳「宽容解释」建议时,务必确保自己的解释与下游 MUA 的解释保持一致,否则宽容反而会制造新的分歧。
参考链接
- RFC 7103 英文原文:https://www.rfc-editor.org/rfc/rfc7103.txt
- IETF Datatracker 页面:https://datatracker.ietf.org/doc/html/rfc7103
- 邮件格式 RFC 5322:https://www.rfc-editor.org/rfc/rfc5322
- MIME 第一部分 RFC 2045:https://www.rfc-editor.org/rfc/rfc2045
- 邮件提交 RFC 6409:https://www.rfc-editor.org/rfc/rfc6409
- 滥用举报格式 RFC 5965:https://www.rfc-editor.org/rfc/rfc5965
