安全限制


JavaScript 可以在浏览器执行程序,这就牵涉到许多安全性问题,使用者下载了网页,下载了 JavaScript 代码偷偷地在执行,有些动作必须加以限制,以免使用者的电脑遭窃取数据、读写或破坏,最基本的,浏览器不允许 JavaScript 对客户端电脑作文件访问的动作。

安全还有其他许多因素要考量,举例来说,在过去,浏览器上可以设状态列文本,不过这也会有安全疑虑,因为在某些情况下,使用者可以透过 JavaScript 来变造状态列消息来欺瞒使用者,状态列文本的设定有所疑虑,事实上,新的浏览器也不再包括状态列了。

使用 JavaScript 可以另开窗口,不过没办法开启太小的窗口,这是个很理所当然的考量,因为若可以开很小的窗口,使用者没注意到它,那在这个小窗口中就可以偷偷摸摸作些坏事了。

在浏览器中基于安全,<input>typefile时,你设定的value会被浏览器忽略,只能由使用者亲自选取文件。你透过 JavaScript 来设定文件选取框的value值也没用。

那么,如果想要设定一个按钮,让使用者可以清除选取的文件要怎么作?透过 JavaScript 将value设为空字符串是没用的。不过可以重新要求浏览器创建新的选取框。例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

      <div id="fileupload"><input name="upload" type="file"></div>
      <button id="clear">Clear</button>

<script type="text/javascript">
    document.getElementById('clear').onclick = function() {
        let fileupload = document.getElementById('fileupload');
        fileupload.innerHTML = fileupload.innerHTML;
    };
</script>

</body>
</html>

按此观看执行结果

在上例中,若选取了文件,并按下 Clear 按钮时,获取的innerHTML会是静态的<input name="upload" type="file">,当它被指定回innerHTML,浏览器会重新解析,等于要求浏览器丢掉旧的 DOM,根据指定的 HTML 创建新的 DOM,在使用者的观感中,自然就像是清除了选取框的内容。

再举使用 JavaScript 获取文件大小为例,在 Firefox、Chrome 中,可以透过files.item(0).size特性来获取文件大小。例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

      <input id="upload" name="upload" type="file"><br>
      <button id="size">Size</button><br>
      <span id="console"></span>  

<script type="text/javascript">
    document.getElementById('size').onclick = function() {
        let size = document.getElementById('upload').files.item(0).size;
        document.getElementById('console').innerHTML = 'Size: ' + size;
    };
</script>  

</body>
</html>

按此观看执行结果

旧版 Internet Explorer 无法使用以上方法,在古早的 Internet Explorer 6 是透过创建ImageActiveXObjectScripting.FileSystemObject)来获取文件大小,但因安全性问题,在后来的 Internet Explorer 就行不通了。

类似的限制还有iframe。例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <iframe id="page" name="page" src="https://openhome.cc"></iframe>
    <span id="console"></span>

<script type="text/javascript">
    window.onload = function() {    
        alert(window.frames['page'].document.body.innerHTML);
    };
</script>  

</body>
</html>

你可以获取iframe的内容,不过仅限与这个文件有相同来源的其他网页才可以,例如,放在我的网站就可以,放在 JS Bin就不行。

若没有这个限制,如果你是在内部网域,我指定你内部网域中某个内部网站的网址,就可以获取网页的内容,之后再上传,也就是说,没有这个限制,我就可以偷你内部网站中的东西。

如果使用 Ajax,创建的 Ajax 非同步对象也会有类似的限制,默认只能使用非同步对象来获取与文件相同来源的其他网页,不同来源的文件在过去是直接被禁止的,而新的规范中,必须服务器有支持相对应的协议才可以。

这称之为同源策略(Same-origin policy)。文件相同来源指的是,与目前文件的 URL 协定、主机与埠号相同的其他文件,才可以被 JavaScript 获取,只要 URL 协定、主机与埠号其中一个不同,就不可以获取。

例如若目前文件来源为 http://caterpillar.onlyfun.net:80/Gossip/demo.html。那么以下视为不同源…

  • https://caterpillar.onlyfun.net:80/Gossip/demo.html(协定不同)
  • http://openhome.cc:80/Gossip/demo.html(主机信息不同,即使实例机器是同一台)
  • http://192.168.0.1:80/Gossip/demo.html(视为主机信息不同)
  • http://caterpillar.onlyfun.net:8080/Gossip/demo.html(埠号不同)

<script>src可以指定外部 URL,来自不同 URL 的 .js 文件,可以在同一个页面中运作,调用彼此变量或函数,但它们可获取的页面必须是与目前页面相同来源,而不是与 .js 文件相同来源。

子网域的文件,默认是不同源,但可以透过document.domain设定子网域为同源,document.domain默认就是文件来源,例如若是 http://caterpillar.onlyfun.net:80/Gossip/demo.html,则document.domain就是'caterpillar.onlyfun.net'。默认无法访问 ooo.onlyfun.net、xxx.onlyfun.net 的文件,可以将document.domain设定为'onlyfun.net',如此来自 ooo.onlyfun.net、xxx.onlyfun.net 的文件就会被视为同源,注意!document.domain只能设为顶层网域,不能设其他网域。

以上简介的是一些常见的 JavaScript 安全议题,实际上还有更多,甚至有些浏览器没限制的,也可能被防毒软件或防火墙防堵。


展开阅读全文