“A vicious circle of incompatibility”

Marcin Wichary

不兼容的恶性循环

原文由 Marcin Wichary 发布,订阅该博客

来自 PortalRunner 的一段 16 分钟趣味视频,前提是这样的:

这是一个图片文件,里面是我家猫的照片。但如果我把它重命名为 .MP4,它就变成了一个视频文件——同样是我那只猫的视频。如果改成 .PDF,它又会变成一份包含本视频脚本的文本文档。只要改个文件名,它还可以是一个合法的网页、一个 .ZIP 压缩包,或是一份 PowerPoint 演示文稿。这种文件有时被称为“polyglot”(尽管这个词通常指能在多种编程语言中运行的代码)。

这种文件在现实中你大概永远用不上,但它有趣地展示了各种文件格式在文件头和结构上的不同设计思路——这些是我们平时很少会去思考的东西。

视频里还藏着一个有趣的岔开话题:文件扩展名仅仅是一种把文件交给正确应用的方式吗?如果我把 .jpeg 改成 .gif,而两者都会用 Pixelmator 打开,那么 Pixelmator 是应该在底层尽力识别出它其实是个 JPEG 文件,还是应该直接报错,提示“这看起来不像是个 GIF 文件”?

网络也面临着类似的挑战,即MIME 嗅探——“MIME”大致相当于网络世界里的扩展名,“嗅探”则是指仅凭文件内容来判断文件类型,而忽略其他一切——这还带来了一些安全隐患,因为它让不法分子得以把恶意代码伪装成更无害的东西混进来……本质上就是视频里出于好玩而做的事,只不过在这里变成了武器。

这些内容对本博客来说都算相当技术了,不过在关于 MIME 嗅探的维基百科词条里,有一段话引起了我的注意:

[MIME 嗅探至今仍被一些浏览器使用。然而,]由于它让那些没有正确标注 MIME 类型的内容的网站在这些浏览器中看起来也能正常工作,就无法促使人们去正确标注内容,而这反过来又使得这些网站必须依赖内容嗅探才能正常运行,从而形成了与网络标准和安全最佳实践不兼容的恶性循环。

早在 MIME 嗅探出现的几十年前,Jon Postel 就用波斯特定律概括了这一思路的精髓——“发送时要保守,接收时要宽容”——但尽管这条定律颇具吸引力,它也面临着与上面那段话类似的挑战:

一个缺陷可能会固化为事实上的标准。协议的任何实现都必须复刻这种异常行为,否则就无法实现互操作。[…] 在这种环境下确保互操作性,通常被称作追求“逐 bug 兼容”。

波斯特定律谈的是计算机系统间数据的输入与输出,但在我看来,其背后的前提是一个更具普遍性的设计问题,适用于许多其他场景。“接收时宽容”或许会让人觉得是在帮用户,但也可能让用户养成坏习惯,并带来更严重的后果。对于任何涉及这一问题的项目,都值得思考:我们究竟应该不遗余力地帮用户兜底,即便他们犯了错;还是应该更严格一些,教会他们更严谨地遵守规则,因为这对他们长远而言更有益?

命令行界面指南里就有一个很好的例子,我之前也分享过

你可以询问用户是否要执行建议的命令,但不要替他们强行执行。例如:

$ heroku pss
› Warning: pss is not a heroku command.
Did you mean ps? [y/n]:

比起仅仅建议正确的语法,你或许会想直接帮他们运行,就好像他们一开始就输对了一样。有时这样做是对的,但并非总是如此。

首先,无效输入并不一定意味着简单的拼写错误——它常常可能意味着用户犯了逻辑错误,或是用错了 shell 变量。擅自揣测用户的意图可能是危险的,尤其是当执行结果会改变状态时。

其次,要意识到,如果你擅自改动了用户的输入,他们就学不会正确的语法。实际上,你等于是在认定他们那样输入也是有效且正确的,并且你承诺会永远支持这种写法。做出这个决定时要慎重,并把两种语法都记录下来。

本文章由 muse-spark-1.2-contributor 进行翻译

评论