如何假装能正确处理 Content-Disposition

Content-Disposition 可能是浏览器、下载器等应用最难正确处理的东西之一。
实际上谁也没能做到完全正确处理,那么怎么在大多数情况下正确处理呢?

标准版的是这样的:
Content-Disposition: attachment; filename="attachment.zip"
这个是完全符合 RFC 2183 规范的版本,基本上也只能在 RFC 2183 里见到了。

Google 决定用正则表达式匹配:
attachment;\s*filename\s*=\s*\"([^\"]*)\"
这个是完全匹配了标准格式,理想状态下成功率是 100%。

于是有网站决定这样发:
Content-Disposition: attachment; filename=attachment.zip
不带引号,匹配失败了。

大概是意识到失败率太高,Google 又修改了一下:
attachment;\s*filename\s*=\s*(\"?)([^\"]*)\1\s*$

于是有网站决定这样发:
Content-Disposition: attachment; filename=attachment.zip; filename*=UTF-8''%E9%99%84%E4%BB%B6.zip
不带引号,匹配错了。

于是又有人放弃了正则表达式,使用简单的 Lexer 切割出分号分隔的 filename 字段。

于是有网站决定这样发:
Content-Disposition: attachment; filename=附件.zip
这种方法的精妙之处在于无法预测它使用了什么编码方式。
这个网站特别注意了绝对不在 Content-Type 中包含任何编码信息,以防被嗅探到正确的编码。
虽然标准规定了必须是 UTF-8 编码,但谁会去看标准呢?

不过如果他们看了标准,发送方式可能会变成这样:
Content-Disposition: attachment; filename="=?ISO-8859-1?Q?a?="
这是 RFC 2047 规定的 Encoded Word 编码方式。

或者干脆组合起来,比如这样:
Content-Disposition: attachment; filename*=UTF-8''%E9%99%84%E4%BB%B6.zip; filename="附件.zip"

这还是假设网站发送的内容都是正确的情况下。
所以,有时候还是假装没看见好了。

顺带一提,如果发回的内容带有斜线等神奇字符,一不小心可就是路径遍历漏洞了。万一还放在了某些敏感位置,可能就开机启动了呢。