摘要

  • RFC 1806 为 MIME 增加 inline、attachment 与可选 filename,表达的是发送方期望的呈现方式和建议名称,不是显示、保存或打开已经发生的证明。
  • 标准明确要求接收端忽略目录信息、避免覆盖,并防止启动文件、系统文件、命令搜索路径与管道因远端名称而被动触发。
  • RFC 2183、RFC 2231、RFC 6266 与 RFC 7578 扩展了元数据、国际化名称、HTTP 下载与表单上传,但始终没有把本地命名空间的控制权交给远端。

MIME 先解决“带什么”,后解决“接下来怎样”

RFC 1521 让一封互联网邮件能够装下文本、图像、应用数据和多层 multipart 结构。RFC 2045 与 RFC 2046 后来重整并延续了这套语法。接收程序因此能划出每个部分的边界,解码传输形式,也能读到发送方声明的媒体类型。

但“这是什么”并不自动回答“现在要不要给人看”。同一幅图在图形邮件程序里可以嵌入正文,在字符终端里可能只能列成条目;一份应用文件也可能需要在用户明确动作之后才交给外部程序。媒体类型描述材料,界面与安全策略仍掌握下一步。

1995 年 6 月发布的实验性 RFC 1806 正面补上这个缺口:任何 MIME 实体都可以带一个可选的 Content-Disposition 字段,说明期望的呈现语义。如果字段不存在,邮件用户代理依然可以自行选择合适的处理方式。

这个缺省规则说明权力关系没有倒置。发送方得到的是表达意图的通道,不是遥控接收端屏幕或磁盘的接口。接收程序拥有设备能力、本地策略、文件系统和用户交互,因此它仍是最后的执行者。

两个词只安排下一道门

最初的词汇很小。inline 表示某个正文部分通常应在普通阅读流程中自动呈现,但仍受所在 multipart 容器规则约束。attachment 表示在呈现前应再有一次用户动作。图形界面可以显示图标,终端可以给出可选择的清单;标准没有规定必须长成哪一种界面。

字段并不观测结果。inline 不能证明接收方支持该媒体类型、通过了安全检查,或真的把像素呈现在人面前;attachment 也不能证明用户点击、保存或打开。一个是远端声明,一个是后续本地事实,两者不能共用成功状态。

嵌套结构进一步拆开了它们。若 disposition 作用于 multipart 容器,它先控制整个容器的呈现。外层若是附件,用户先要打开外层;只有到了那一步,内部各部分的 disposition 才有意义。一条消息于是可以含有连续多道门,外层标签不构成内层已被访问的证据。

遇到不认识的 disposition,RFC 1806 要求按附件处理。新词不会仅凭未知身份获得自动显示权限。不认识的参数则可以忽略。这个回退没有虚构语义,只把不确定性放在需要额外动作的一侧。

一个参数不能替文件系统签收

可选 filename 为“如果用户选择拆出并保存”这一条件提供默认归档名称。它甚至可以出现在 inline 部分上,因此字段存在时,磁盘上完全可能什么也没有发生。把它记录成“已保存文件名”,等于把条件句改成了完成时。

RFC 1806 要求接收程序不采信名称里的目录信息,只把可用的末端部分按本地习惯处理,并阻止覆盖已有文件。原因不是某种操作系统特别危险,而是名称的含义由接收端决定:路径分隔符、驱动器写法、保留设备名、可写目录、扩展名关联和已有文件集合都在远端视野之外。

标准把风险列得很具体。恶意名称可能创建启动文件,覆盖系统文件或用户已有文件,把可执行内容放入命令搜索路径,甚至把内容送进管道。接收程序不应让命名或放置本身在没有用户明确发起的情况下造成解释与执行。

因此,一次完整审计至少要保留五层事实:发送方发来了什么原始字符串;解析器怎样识别参数;本地策略删掉了哪些路径含义;文件系统最终是否写入、写到哪里;某个应用之后是否解释或执行了内容。首层证据不能替后四层签字。

标准化增加了属性,没有增加命令权

RFC 1806 的资料页显示,1997 年 8 月,RFC 2183 以 Proposed Standard 身份取代它。新版保留 inline、attachment 和接收方负责检查文件名的原则,并增加创建时间、修改时间、读取时间与近似大小。

这些字段让传输对象更像一张文件信息卡,却没有把它变成可信的文件履历。RFC 2183 特别提醒 Unix 与 POSIX 实现者,st_ctime 不是创建时间。日期依旧是随消息而来的主张,接收方可以决定是否保留。近似大小可以帮助估算空间,却不是内容摘要、空间分配保证或完整写入回执。

RFC 2183 还引入 IANA 扩展登记程序。今天的 Content Disposition 登记表收录早期值以及后来针对不同协议位置定义的值。登记能够证明一个词有公开定义和维护入口,不能证明某个客户端实现了它,也不能证明同一个词在每个上下文里都可互换。

人的名字装不进最初的 ASCII 盒子

RFC 1806 已承认,初始参数语法把文件名限制在 US-ASCII。对于本来要帮助人识别内容的名称,这使大量正常文字无法直接表达。RFC 2231 为长参数提供编号续段,也提供带字符集、可选语言和百分号编码字节的扩展形式。

名字能写得更完整,解析过程也多出必须留痕的分岔。续段可能缺失或乱序,编码段与普通段可能混合,字符集可能未知或无效。语言标签可以辅助呈现,却不会改变原始字节身份。最终 Unicode 字符串是一次重组与解码的结果,不等于报文里的原样字段。

更准确的编码不会把危险名称洗白。被正确还原的目录穿越仍是目录穿越,被正确显示的保留设备名仍可能危险。RFC 2231 解决的是表示能力,不是授权范围。

Web 先使用,后来才正式收编

到 1999 年,RFC 2616 已指出,虽然 Content-Disposition 当时还不是 HTTP 标准的一部分,它在 HTTP 中却经常被实现。Web 服务器遇到了与邮件发送方相似的需要:希望某个表示作为下载处理,并给人一个有用的建议名称。

2011 年的 RFC 6266 正式定义 HTTP 响应字段。它明确说文件名只是建议。用户代理必须防止服务器选择无权访问的位置,只保留路径最后一段,警惕会导致危险后续处理的扩展名,剔除控制字符或迷惑性空白,并中和对文件系统或 shell 有特殊含义的名字。

对于国际化名称,响应可以同时给出兼容用 filename 和扩展 filename*;理解两者的接收端应优先使用后者。RFC 8187 后来收紧 HTTP 扩展参数编码并要求支持 UTF-8。优先级本身也要有证据:两个原值、被选参数、所用解码器与最终显示名不能压成同一个字段。

上传让发送与接收的位置对调

Content-Disposition 也出现在 multipart/form-data 的正文部分里,但这不是 RFC 6266 所管的响应字段;后者明确排除了这一场景。RFC 7578 要求每个表单部分带 Content-Disposition: form-data 和 name 参数,文件部分在有意义时还应带 filename。

这一次,浏览器或上传客户端成为名称发送方,Web 应用成为保护本地存储的接收方。RFC 7578 再次要求不能盲信来名,要适应本地约定并丢掉目录信息。它还明确禁止在这个正文部分上下文中使用 filename*,尽管下载响应会使用它。同一个字段名,不等于同一套位置语义。

从邮件附件到浏览器下载,再到表单上传,真正连续的不是某个按钮图标,而是一条窄窄的权限边界:远端可以说明期望处理方式,也可以递来一个给人看的名称;是否呈现、是否落盘、实际叫什么、是否继续解释,仍由承担本地损失的一方决定。

来源