假如让你实现一个在线的 JavaScript 代码运行环境,要求用户代码不能对页面进行修改,以避免潜在的安全问题,你会怎么做?
使用 ?使用 ?OK ,都可以,但是这两种方法都需要关注很多细节,否则用户依旧有可乘之机,这样一来你的实现里面就会有一个很长长长长长长长的操作黑名单。
除此之外,我们还可以专门部署一个页面,将代码提到服务端渲染成页面,再通过 iframe 去访问,如果 iframe 与父页面之间是跨域的话可以达到很高的安全性——那么能不能不看后端的脸色,完全使用浏览器来实现类似的沙箱呢?
当然可以——
对前端页面而言,跨域是页面与页面之间的鸿沟,但这并不意味着我们必须重新打开一个页面来运行新的代码,因为我们可以使用 标签:
对于同域的 iframe ,我们可以直接通过 访问并操作它的全局对象,然后直接往里面执行 JavaScript:
.contentWindow
.eval(‘alert(“hello world!”);’);
但是同域页面的子页面是可以与父页面进行互操作的,
你可能在一些页面里见过小图片不使用网络链接,而是采用一个 风格的 URL ,这种 URL 就是 data URL。
除了 data URL 之外你可能还见过 开头的 URL —— Object URL。不过 Object URL 与当前页面是同域的,而 data URL 与当前页面是跨域的。所以我们可以在 iframe 使用 data URL 来进行跨域隔离。
我们可以直接将 JavaScript 片段变成 的 URL ,但是这样有一个问题: iframe 打开这样的 URL 的时候,会显示代码原文而不是执行代码,这个行为其实和你直接在浏览器地址栏输入 JS 的 URL 是一样的。
所以我们需要将 JavaScript 代码拼接到 里面,再变成 data URL ,然后交给 iframe 去加载:
alert(‘hello world’);
`;
const htmlFragment=`
<!doctype html>
<html>
<head>
<meta chatset=”utf-8″ />
</head>
<body>
<script>${javaScriptFragment}</script>
</body>
</html>
`;
const dataUrl=`data:text/html,${htmlFragment}`;
// 注意,如果代码片段中含有中文的话,需要使用 encodeURIFragment 转义 htmlFragment
document.querySelector(‘iframe’).src=https://www.jb51.net/javascript/dataUrl;
如果我们不但要做沙箱隔离,还被要求获取运行结果的话,则可以做一个通信机制,让 iframe 获取到用户代码执行结果, iframe 与父页面之间最好的跨域通信方法莫过于 。
除了获取结果之外,还可以将通信机制进一步扩展成为 RPC ,这样可以实时修改页面里的代码来查看效果,类似于 codepen 。
具体实现与主题不是强相关,这里就不写了,更多关于JavaScript iframe页面跨域隔离的资料请关注脚本之家其它相关文章!