-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
如何禁止开发者操作网页上的DOM对象? #20
Labels
Comments
如果要完全创建一个只可以访问指定对象的沙盒,月影有啥好办法没有呢?我们现在的做法是使用了一个脚本解析器 https://github.com/NeilFraser/JS-Interpreter |
shadow dom 的兼容性不是很好 您那个worker方法我没有看的太明白? 怎么让用户的脚本 强制都套用那个 "execCodeInWorker" |
用户脚本是保存在userCode变量里的,execCodeInWorker是你自己的代码而不是用户写的 |
@libmw 用脚本解析器是可以的,一般也就是选择使用脚本解析器,就是有额外的开销(时间、空间)。 |
;(function () {
// shadow global object, like window、document
const shdowGlobals = []
// valid context (合法上下文,即允许用户调用的API)
const context = {}
const contextVars = Object.keys(context).map((key) => `${key}=context.${key}`)
const fn = new Function(
'context',
`
var ${shdowGlobals.join(',')}
var ${contextVars.join(',')}
context = undefined
;(function() () {
// user code (用户代码)
}).call({})
// prevent this by call (阻止通过 this 访问全局API)
`
)
fn(context)
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
通常情况下,在一个HTML页面上,我们总能够通过DOM API访问想要访问的HTML元素,进行操作。
如果基于某种原因,允许用户注入代码到网页上,但又要禁止用户对DOM对象进行操作,即只允许用户调用我们提供的API,不允许用户通过注入的JS来修改我们创建的UI组件甚至整个网页内容,那么我们要怎么做呢?
我们基本上无法通过JS来禁止用户通过注入的JS操作DOM,因为window对象、document对象这些对象以及它们的API是无法通过JS改写的:
如果我们用
Object.getOwnPropertyDescriptor
查看,会发现window.document
实际上是一个getter,而且它的configurable是false。即使我们对允许用户合法注入的JS外面包裹函数作用域,我们还是无法彻底阻止用户访问document和window对象。
比如说上面的代码,我们在用户注入的代码前后增加包装代码,把window和document对象通过函数参数覆盖,我们还是能通过函数调用的this拿到window对象。
要防住这个漏洞,我们可以在包装的时候使用严格模式:
但是这个依然不解决问题:
所以结论是包装代码无法让用户彻底无法访问window和document对象。
那么我们要怎么做既能合法让用户注入代码实现功能,又能够隔离window和document对象呢?
用worker做沙箱
第一种办法是只允许用户的代码跑在worker里。
我们知道worker的环境是和浏览器环境互相独立的线程,所以跑在worker里的代码是不能访问window和document对象的,这样就保证了安全性。
使用worker的问题是,当worker和浏览器环境通讯时,需要采用postMessage,如果有较多的交互操作,性能开销比较大,而且对写代码的开发者有使用成本。
使用Shadow DOM
如果我们只是不允许用户注入的JS修改UI,我们也可以将整个UI通过Shadow DOM渲染,并且将ShadowRoot的模式设置为
closed
封闭起来,这样的话,用户就无法拿到Shadow DOM的ShadowRoot对象,从而无法进行操作。下面是一个简单的例子:
我们通过
document.body.attachShadow({mode: 'closed'});
创建ShadowRoot,通过Shadow DOM API来创建UI,因为这个root对象我们没有暴露给用户,而且mode是closed,所以用户拿不到对象,无法通过DOM操作我们的UI,只能通过我们暴露给用户的addTask和removeTask来操作。💡注意,用户当然仍可以通过DOM API来往body中插入其他内容,但是,当一个元素创建了Shadow DOM,浏览器会优先渲染Shadow DOM,而忽略它的其他子元素,所以用户往body中插入任何内容都不会被渲染出来。唯一的例外是如果插入script标签,脚本会被执行,但是我们可以简单通过防止xss的代码过滤来阻止用户插入script标签。
这样,我们就通过Shadow DOM获得了一个相对安全的环境,对比worker的方式,可以避免postMessage的开销和拥有更简单的写法。当然Shadow DOM也有弊端,比如用户虽然不能改写当前body元素中渲染的内容了,但是可以彻底删掉body元素重新创建一个:
但是那样的话,原body中的所有内容也需要重建了。因此,使用Shadow DOM API至少大大增加了用户侵入的成本。
关于禁止开发者操作DOM对象的话题就谈到这里,还有什么可行的方法或者大家有什么想补充的,欢迎在issue中讨论。
The text was updated successfully, but these errors were encountered: