摘要

  • RFC 2231 让较长的 MIME 参数从 *0 开始连续分段,并只在首个编码段声明字符集与可选语言。接收端必须按编号而非出现顺序重组完整序列,然后再解释百分号编码和字符集。
  • 重组成功只说明一个文本值可以被恢复。RFC 2183 明确把 filename 当作保存建议;路径、覆盖、特殊文件、扩展名和执行动作仍要经过独立的本地安全策略。

后到的第一段

设想邮件头中先出现 filename*1*,后出现 filename*0*,最后才是 filename*2。这是完全可能的,因为 MIME 参数的排列顺序本来就没有语义。真正有语义的是星号后的数字。

下面这组仅用于说明的参数,表达的是一个值,而不是三个文件名:

filename*0*=utf-8'en'Quarterly%20; filename*1*=review%20; filename*2=final.pdf

首段从零开始,给出 UTF-8、可选的语言标记 en,以及值的开头;第二段继续携带百分号编码的字节;第三段没有编码标记。只有当编号从零连续递增、没有缺口也没有含糊的重复时,接收端才有条件按 0、1、2 的次序连接,得到 Quarterly review final.pdf。

顺序不能颠倒。先把每段各自转成字符串,再做连接,看上去省事,却把传输切片误当成字符边界。RFC 2231 只允许字符集和语言信息出现在开头;一个多字节字符的字节完全可能跨过段界。如果两个实现分别在段内解码,它们可能产生替换字符,也可能得到不同文本。更可靠的处理链是:识别同一参数名;验证连续编号;按编号拼接载荷;把带编码标记的百分号序列还原为字节;按首段声明的字符集解释;最后才把结果交给内容处置策略。

RFC 2231 自己的安全章节很短,也没有把这套次序包装成今天常说的安全 API。这里的处理链是从它的语法、单一首段字符集声明和跨段字节事实推导出的实现边界。它的价值恰恰在于把问题拆开:结构是否完整、字节怎样还原、文本怎样解释,回答的是三件不同的事。

文件名不是远程写盘指令

即使解码毫无歧义,filename 仍不能直接成为本地路径。RFC 2231 延伸了参数表示方式,却没有改变 RFC 2183 对 Content-Disposition 的权力边界。

RFC 2183 把文件名描述为保存正文部分时可采用的建议名称。接收邮件程序不能盲目照用。若值中带目录路径,应当只保留末端名称;还要防止覆盖既有文件,避免写入系统或启动位置,警惕管道和特殊文件,并且不能因为文件被保存就让它在没有用户明确动作的情况下执行。

这些要求在国际化重组成功之后一项也不会消失。合法的 UTF-8 文本仍可能包含 ../、绝对路径、双向控制字符、保留设备名、误导性后缀或不可见字符。百分号解码不等于清洗;字符集正确不等于发件人身份真实;filename 与 Content-Type 一致也不等于内容安全。更不能因为一个标准语法被接受,就推导出覆盖、打开、执行或归属的授权。

因此接收链应当保留多道独立闸门。解析器只确认续段结构;解码器只确认字节与文本的对应;命名策略生成或挑选安全的本地名称;存储层把目的地约束在允许的目录并处理冲突;用户或另一个获授权策略才决定是否打开或执行。前一道成功,不能替代后一道判断。

作者名单也需要准确重组

RFC 2231 的作者栏写的是 Ned Freed 与 Keith Moore。它所取代的 RFC 2184 也由这两人署名。这就是 MIME 参数续段机制的准确共同作者边界。

Patrik Fältström 在互联网国际化史上有重要位置,但不是 RFC 2231 或 RFC 2184 的作者。RFC 3490 这份早期 IDNA 规范由 Fältström、Paul Hoffman 与 Adam Costello 署名。把相邻领域的贡献合并成同一份 RFC 的作者关系,会抹掉标准文件最基本的来源边界:议题可以相邻,作者与协议合同却不能凭印象串联。

Freed 更广泛的 MIME 工作有充足记录。IETF Datatracker 展示了他长期参与邮件与媒体类型标准的轨迹。Nathaniel Borenstein 在追思文章中回忆,两人的合作一边追求更丰富的邮件,一边强调鲁棒性与不同系统互操作。RFC 2045 规定消息正文的 MIME 格式,RFC 2047 处理消息头中的非 ASCII 文本;RFC 2231 则补上仍然棘手的参数值问题。

范围变窄不是缺陷。星号后缀并不会自动让所有协议字段获得同一种含义,采用它的规范必须明确选择。RFC 2231 也没有把文件名变成身份声明。它解决表示与互操作,让内容处置和本地安全继续由别的规则负责。

HTTP 保留编码,放弃续段

后来的 HTTP 规范把这个边界说得更清楚。RFC 8187 延续带字符集的参数编码形式,并要求支持 UTF-8,但没有采用 RFC 2231 的续段机制,因为 HTTP 并不需要它。技术血缘存在,合同范围却有意缩小。

RFC 6266 对 HTTP Content-Disposition 的 filename 再次强调“仅供参考”。接收者应丢弃路径片段,自行控制目标目录,防止后缀赋予可执行含义,移除令人混淆的控制字符并处理特殊名称。服务端可以建议标签,却不能借一段响应取得客户端文件系统的控制权。

这也是 RFC 2231 留下的更深层价值:互操作要求在表示边界上严格——从零编号、连续无缺口、首段声明一次字符集、确定的重组与解码顺序;安全则要求同样严格地限定这些事实证明不了什么。字符串可以被完整恢复,但不会因此变得可信。元数据可以有意义,却不能因此成为主权。

来源