这是代码:
function secure_eval(s) { var ret; (function(){ var copyXMLHttpRequest = XMLHttpRequest; // save orginal function in copy XMLHttpRequest = undefined; // make orignal function unavailable (function() { var copyXMLHttpRequest; // prevent access to copy try { ret = eval(s) } catch(e) { console.log("Syntax error or illegal function used"); } }()) XMLHttpRequest = copyXMLHttpRequest; // restore original function }()) return ret; }
这样做的工作如下:
secure_eval('new XMLHttpRequest()'); // ==> "illegal function used"
现在我有几个问题:
这种模式是正确的方法来保护eval吗?
>窗口和文档的哪些功能被认为是有害的?
>绕过问题2.我试图掩盖窗口的所有(本机)功能但是我无法枚举它们:
这不列举XMLHttpRequest:
for( var x in window) { if( window[x] instanceof Function) { console.log(x); } }
编辑:
我的一个想法是在Worker中执行eval,阻止访问XMLHttpRequest和document.createElement(请参阅我的上面的解决方案).这将(在我看来)以下后果:
>无法访问原始文档
>无法访问原始窗口
>没有机会与外部资源进行沟通(没有ajax,没有脚本)
你在这里看到什么缺点或泄漏?
EDIT2:
在此期间,我发现了this question这个答案解决了我的许多问题,还有一些我甚至没想到的事情(即浏览器死锁与“while(true){}”).
解决方法
secure_eval("secure_eval = eval"); // Yep,this completely overwrites secure_eval. secure_eval("XMLHttpRequest()");
要么:
secure_eval("new (window.open().XMLHttpRequest)()")
要么:
secure_eval("new (document.getElementById('frame').contentWindow.XMLHttpRequest)()")
第三种方法依赖于页面HTML中存在iframe,有人可以通过在浏览器中操作DOM来添加.我每时每刻都在进行这样的操作,然后用Greasemonkey来消除烦恼或修复损坏的GUI.
这花了我大约5分钟的时间来弄清楚,我不是一个安全上师.这些只是我能够快速找到的洞,可能还有其他人,我不知道.这里的教训是,真的很难通过eval来保护代码.
使用工人
好的,所以使用Worker来运行代码将会照顾第二和第三种情况,因为在Worker中没有可访问的窗口.和…嗯..第一种情况可以通过在其范围内的shadowing secure_eval来处理.故事结局?要是…
如果我把secure_eval放在一个web worker里面运行下面的代码,我可以重新获取XMLHttpRequest:
secure_eval("var old_log = console.log; console.log = function () { foo = XMLHttpRequest; old_log.apply(this,arguments); };"); console.log("blah"); console.log(secure_eval("foo"));
原则是覆盖一个在secure_eval之外使用的函数来捕获XMLHttpRequest,将其分配给一个将被故意泄露给工作人员的全局空间的变量,等到该函数被secure_eval外的工作人员使用,然后抓住保存价值.上面的第一个console.log模拟了在secure_eval之外使用篡改的函数,第二个console.log显示该值被捕获.我使用了console.log,因为为什么不呢?但是,真正的全局空间中的任何功能都可以像这样修改.
其实为什么要等到工作人员使用我们篡改的一些功能?这是另一个更好,更快捷的方式来访问XMLHttpRequest:
secure_eval("setTimeout(function () { console.log(XMLHttpRequest);},0);");
即使在一个工作人员(使用原始的console.log)中,这将输出XMLHttpRequest的实际值到控制台.我还会注意到,传递给setTimeout的函数中的值是全局范围对象(即不在工作中的窗口,或工作中的自身),不受任何变量阴影的影响.
这个问题提到的其他问题呢?
解决方案here?更好,但在Chrome 38中运行时仍然有一个漏洞:
makeWorkerExecuteSomeCode('event.target.XMLHttpRequest',function (answer) { console.log( answer ); });
这将显示:
function XMLHttpRequest() { [native code] }
再一次,我不是安全大师或者破解者,就是为了造成麻烦.可能还有更多的方法我不在想.