JavaScript 的陷阱
理查德·斯托曼 著网页可以有两种道德错误。本页描述的是其中一种:网页向电脑发送并运行非自由软件。另外一种错误我们称之为 SaaSS——“服务代替软件”——网页请你 把数据发过去,这样它就可以在服务器上做计算——这种计算是不公正的,因为你没有对计算的控制权。
你可能每天都在自己的电脑上运行非自由软件却毫不知情——通过使用你的浏览器。
在自由软件社区,非自由软件会侵害其用户的概念大家都很熟悉。有些人会拒绝在电脑上使用任何专有软件,以此来捍卫自己的自由。还有许多人意识到非自由并拒绝那些程序。
许多用户知道浏览器要求安装的插件有此问题,因为它们可能是自由软件,也可能是非自由软件。但是浏览器也会运行不要求你许可的其他非自由软件,甚至也不告诉你——网页带有或链接了哪些程序。这些程序多数是用JavaScript编写的,虽然其他语言也是可以的。
JavaScript(正式名称是ECMAScript,但是很少有人用了)曾经用于不重要的网页装饰,比如可爱但是并不重要的导航和显示功能。可以认为这些只是HTML标记语言的扩展,而不是真正的软件,这样就可以忽略它的问题。
一些网站仍然这样使用JavaScript,但是有一些网站用它作为完成任务的主要程序。例如,Google Docs会尝试在你的电脑里下载大约500k字节的JavaScript程序,该程序使用一种我们称之为模糊脚本(Obfuscript)的紧密格式。这种格式删除了源代码里有助阅读的空格和注释,并将原来有意义的方法名称替换为任意的短名称,这样人们就无法直接看出这些方法原来的意义。
自由软件定义的内容包含着用户可以访问程序的源代码。一个程序的源代码应该是有利于修改程序的形式—包括有利阅读的空格和分行、解释性注释和有意义的名称。这种紧密格式不是源代码,而只是毫无用处的源代码替代品;其源代码并没有提供给用户,因而用户无法了解该程序;该程序是非自由的。
除了是非自由软件,许多这种程序是恶意软件,因为它们嗅探用户。更恶劣的是,有些站点使用服务来记录用户浏览该页面时的所有行为。该服务本来应该“修改”记录以抹去网站不该获得的一些敏感数据。但是,即使该功能能够可靠的实施,该服务的整体目的也是给予该网站它不应该获得的个人数据。
浏览器一般不会告诉你它们什么时候会加载JavaScript程序。有些浏览器提供了完全禁用JavaScript的方法,可是即使你知道有问题,你还是需要花很多精力去确认这些非自由软件并拦截它们。然而,就连自由软件社区的大多数用户都不知道这个有问题;而浏览器的沉默也好似在掩盖这个问题。
澄清一下,JavaScript 语言本身和其他语言一样,并不影响用户的自由。JavaScript 程序也能发布为自由软件,使用自由软件许可证发布其源代码就行。如果该程序是自给自足的——如果它的功能和目的独立于它所应用的网站——这就没问题;你可以将其拷贝到你的电脑,修改它,使用浏览器加载它并运行它。你甚至可以把 JavaScript 象其他自由软件一样打包,并在命令行调用。这些程序和 C 程序一样并没有特别的道德问题。
JavaScript 陷阱适用于用户访问网页时网页带有的 JavaScript 程序。这些 JavaScript 程序专门为特定网页或网站编写,这些网页或网站依赖这些程序才能工作。
假定你可以复制和修改这些 JavaScript程序。这就出现了另一个问题:即使有源程序,浏览器也无法运行你修改的版本,它只支持使用原来的版本访问该网站或网页。这个效果和 tivoization1类似,虽然从根本上不象那么难以解决。
JavaScript 不是网站发送到用户的程序使用的唯一语言。Flash 支持用一种扩展的 JavaScript 语言编程,不过这个已经是历史了。Silverlight 似乎也和 Flash 一样创建一些程序,可能更糟,因为微软使用它作为非自由编解码器的平台。自由的 Silverlight 替代不能为自由世界正常工作,除非它还带有自由的编解码替代。
Java小程序2也会在浏览器里运行,也就有同样的问题。一般来说,各种小程序系统都有这样的问题。创建一个运行小程序的自由环境仅仅是让我们能够看到这个问题。
理论上能够使用HTML和CSS编程,但是实际上它不方便而且受限制;最多只是展示一些令人惊奇的黑客编程技巧。这些程序理应是自由的,不过CSS在2019年还不是影响用户自由的严重问题。
已经有一个强大的运动,它号召网站只使用自由(有的人叫“开放”)的格式和协议来通信;就是说,它们的文档应该公开,而且任何人都有自由来实现。然而,当网页带有JavaScript程序时,这个标准就不够了。JavaScript本身,作为一种格式,是自由的,而且在网站上使用JavaScript并不必然是坏事。然而,正如以上所看到的,它也不必然就是好事——如果JavaScript程序是非自由软件。当网站将程序转移到用户,程序只是由有文档、不受限制的语言编写并不足够;该程序必须也是自由的。“只许把自由软件转移到用户”必须成为网站行为规范的准则。
不动声色地加载和运行非自由软件只是“网络应用”带来的诸多问题之一。“网络应用”这一术语就是用来抹杀用户软件和服务器软件的根本区别。它可以指运行在浏览器里的特定客户端软件;它也可以指特定的服务器软件;它还可以指和特定服务器软件紧密协作的客户端软件。客户端和服务器端有不同的道德问题,即使它们如此紧密地协作,以至于可以说它们是一个软件的不同部分。本文只讨论客户端软件的问题。我们会分开讨论服务器端的软件。
实际操作中,我们应该如何对付网站上非自由、非平凡的JavaScript程序带来的问题呢?第一步就是避免运行它。
什么是我们说的“非平凡”呢?它表示的是程度,所以它是指设计一种简明的标准来获得好的结果,而不是去寻求唯一正确的答案。
我们目前的标准认为一个JavaScript程序是非平凡的,如果它满足以下任一条件:
- 该程序引用一个外部的脚本(来自另一个页面)。
- 该程序声明的数组超过了50个元素。
- 该程序定义的主体(函数或方法)会做调用基本函数以外的调用。
- 该程序定义的主体带有3个以上的条件判断或循环结构。
- 除了定义之外,页面的代码调用了该页面上间隔很远的非基本函数。
- 除了定义之外,页面代码带有3个以上的条件判断或循环结构(两个结构的总和超过3)。
- 该程序调用了 eval。
- 该程序有 Ajax 调用。
- 该程序使用带有方括号形式的动态对象属性访问,就像object[property]这样。
- 该程序修改了 DOM。
- 该程序要么使用动态JavaScript结构,这些结构在不了解该程序的情况下很难分析,要么该程序和使用这些结构的脚本一起加载。具体情况是程序使用除了诸如带有方法(Obj.write, Obj.createElement等)之外的字符串字面量。
我们如何判别JavaScript代码是否是自由的?在另一篇文章中,我们提议了一种方法,它指出网页带有的非平凡JavaScript程序可以使用风格化的注释来表达获得其源代码的URL,也可以表达其许可证。
最后,我们需要修改自由的浏览器,使它们可以探测并拦截网页上非平凡、非自由的JavaScript。LibreJS程序就可以探测到你访问的网页上的非自由、非平凡的JavaScript,并拦截之。LibreJS包含在IceCat浏览器中,也可以作为Firefox的附加组件。
浏览器用户还需要方便的工具来指定需要使用的JavaScript代码,而不是使用网页上的JavaScript代码。(指定的代码可以是完全的替代,也可以是页面上的自由JavaScript程序的修改版。)Greasemonkey已经快能够做到这一点,但是还没有,因为它不能保证在页面上的JavaScript程序运行之前就做出修改。使用本地的代理可以工作,但是作为真正的解决方案还是太不方便。我们需要构建一个可靠和方便的解决方案,也要构建一个分享修改方法的网站。GNU工程愿意推荐那些只致力于分享自由修改方法的网站。
这些措施使网页带有的JavaScript程序变成实际和真正意义上的自由软件成为可能。JavaScript就不再是阻碍我们自由的特别障碍—就象现在的C和Java那样。我们将来能够拒绝、甚至替换掉非自由和非平凡的JavaScript程序,就像我们拒绝和替换那些以通常形式安装的非自由软件包一样。那时,我们就能够开始让网站提供自由JavaScript程序的活动了。
与此同时,在一种情形下运行非自由JavaScript程序是可以接受的:使用它来向网站操作员抱怨该网站应该移除JavaScript代码或者使它自由。请不要犹豫,你可以暂时启用JavaScript做这件事—之后别忘了再禁用它就好。
致谢:我感谢 Matt Lee 和 John Resig,他们帮助我们定义了我们提议的标准,还感谢 David Parunakian 提醒我关注此问题。
网络管理员:有好几种方法可以指示网站的JavaScript程序的许可证。