我真的想为用户提供一些脚本功能,同时不让它访问更强大的功能,比如改变DOM。也就是说,所有输入/输出都通过给定接口进行隧道传输。就像一种受限制的javacsript。
实施例:
如果界面是checkanswer(func)
这是允许的:
checkanswer( function (x,y)={
return x+y;
}
但不允许这样做:
<击> alert(1)
document.write("hello world")
eval("alert()")
击>
编辑:我想到的是一种使用javascript实现的简单语言,例如http://stevehanov.ca/blog/index.php?id=92
答案 0 :(得分:14)
(编辑这个答案与您的编辑前问题有关。不知道使用Javascript实现的任何脚本语言,虽然我希望有一些。例如,有人写过BASIC对于Javascript(曾经有一个链接,但它已经腐烂)。因此,这个答案的其余部分非常具有学术性,但我只是为了讨论,插图,甚至是警示目的而留下它。而且,我绝对同意{{3 - 不要自己动手,使用其他人的工作,例如bobince's points。)
如果您允许在用户生成的内容中使用任何脚本,那么请准备好您将进入军备竞赛,让人们在您的保护机制中找到漏洞并利用它们,并回应这些漏洞。我想我可能会回避它,但你知道你的社区和你处理虐待的选择。所以如果你准备好了:
由于Javascript执行符号解析的方式,似乎应该可以在window
,document
,ActiveXObject
,{{1}的上下文中评估脚本},和类似的没有它们通常的含义:
XMLHttpRequest
(现在使用了邪恶的// Define the scoper
var Scoper = (function() {
var rv = {};
rv.scope = function(codeString) {
var window,
document,
ActiveXObject,
XMLHttpRequest,
alert,
setTimeout,
setInterval,
clearTimeout,
clearInterval,
Function,
arguments;
// etc., etc., etc.
// Just declaring `arguments` doesn't work (which makes
// sense, actually), but overwriting it does
arguments = undefined;
// Execute the code; still probably pretty unsafe!
eval(codeString);
};
return rv;;
})();
// Usage:
Scoper.scope(codeString);
,但我不能立即想到一种方法来在不使用eval
的情况下跨浏览器隐藏默认对象,如果你收到的代码是无论如何......)
但不起作用,它只是部分解决方案(更多内容)。逻辑是,eval
中代码中的任何尝试访问codeString
(例如)将访问局部变量window
,而不是全局变量;和其他人一样。不幸的是,由于解析符号的方式,window
的任何属性都可以使用或不使用window
前缀(例如window.
)进行访问,因此您也必须列出这些属性。这可能是一个很长的列表,尤其是因为Caja指出,IE将任何带有名称或ID的DOM元素转储到alert
。所以你可能不得不把所有这些都放在自己的iframe中,这样你就可以在那个问题上做一个最终的运行,而“只”必须处理标准的东西。另请注意我如何将window
函数作为对象的属性,然后仅通过属性调用它。这样scope
设置为this
实例(否则,在原始函数调用中,Scoper
默认为this
!)。
但是,正如鲍勃指出的那样,有很多不同的方法可以解决问题。例如,window
中的这段代码成功打破了上面的监狱:
codeString
现在,也许你可以更新jail以使该特定漏洞利用不起作用(对所有<{1>}所有的所有<{1}}属性进行捣乱内置对象),但我倾向于怀疑它。如果你可能,某人(比如Bob)会想出一个新的漏洞利用,就像这个:
(new ('hello'.constructor.constructor)('alert("hello from global");'))()
因此“军备竞赛”。
唯一真正彻底的方法是在您的网站中内置一个正确的Javascript解析器,解析他们的代码并检查非法访问,然后让代码运行。这是很多工作,但如果你的用例证明它......
答案 1 :(得分:5)
T.J。克劳德对“军备竞赛”提出了一个很好的观点。 非常很难建立一个防水沙箱。
但是,可以很容易地覆盖某些功能。简单的功能:
根据this question,即使是覆盖document.write
这样的事情就像
document.write = function(str) {}
如果在您需要支持的浏览器中有效(我认为它适用于所有浏览器),这可能是最好的解决方案。
备选方案:
将脚本沙箱放入不同子域的IFrame中。可以操纵自己的DOM并发出alert()等等,但周围的网站仍然保持不变。无论如何,无论选择哪种方法,您都可能必须这样做
使用允许函数的白名单解析用户代码。同样非常复杂,因为有太多的符号和变化需要照顾。
有几种方法可以监视DOM的变化,我很确定可以构建一种立即恢复任何更改的机制,与Windows的DLL管理非常相似。但是构建和资源密集型会非常复杂。
答案 2 :(得分:3)
不是真的。 JavaScript是一种极其动态的语言,具有许多隐藏或特定于浏览器的功能,可用于打破您可以设计的任何类型的监狱。
不要试着自己拿这个。考虑使用现有的“迷你JS类语言”项目,例如Caja。
答案 3 :(得分:0)
听起来您需要处理用户输入的数据并根据白名单或允许内容的黑名单替换无效标记。
答案 4 :(得分:0)
你可以像Facebook那样做。他们正在预处理所有的javascript源代码,为除了自己的包装器API以外的所有名称添加前缀。
答案 5 :(得分:0)
我有另一种方式:使用google gears WorkerPool api
见这个
http://code.google.com/apis/gears/api_workerpool.html
创建的工作人员无权访问 到DOM;文件和文件等对象 窗口仅存在于主页面上。 这是工人没有的结果 共享任何执行状态。然而, 工人可以接触所有人 JavaScript内置函数。最 也可以使用齿轮方法, 通过一个全局变量 自动定义: google.gears.factory。 (一个例外 是LocalServer文件提交者, 这需要DOM。)对于其他 功能,创造的工人可以问 执行请求的主页。
答案 6 :(得分:0)
为了实现沙箱,这个模式怎么样?
function safe(code,args)
{
if (!args)
args=[];
return (function(){
for (i in window)
eval("var "+i+";");
return function(){return eval(code);}.apply(0,args);
})();
}
ff=function()
{
return 3.14;
}
console.log(safe("this;"));//Number
console.log(safe("window;"));//undefined
console.log(safe("console;"));//undefined
console.log(safe("Math;"));//MathConstructor
console.log(safe("JSON;"));//JSON
console.log(safe("Element;"));//undefined
console.log(safe("document;"));//undefined
console.log(safe("Math.cos(arguments[0]);",[3.14]));//-0.9999987317275395
console.log(safe("arguments[0]();",[ff]));//3.14
返回:
Number
undefined
undefined
MathConstructor
JSON
undefined
undefined
-0.9999987317275395
3.14
请提供适合攻击此解决方案的漏洞利用程序?只是为了理解和提高我的知识,当然:)
谢谢!
答案 7 :(得分:0)
现在可以使用沙盒IFrame轻松实现这一点:
var codeFunction = function(x, y) {
alert("Malicious code!");
return x + y;
}
var iframe = document.createElement("iframe");
iframe.sandbox = "allow-scripts";
iframe.style.display = "none";
iframe.src = `data:text/html,
<script>
var customFunction = ${codeFunction.toString()};
window.onmessage = function(e) {
parent.postMessage(customFunction(e.data.x, e.data.y), '*'); // Get arguments from input object
}
</script>`;
document.body.appendChild(iframe);
iframe.onload = function() {
iframe.contentWindow.postMessage({ // Input object
x: 5,
y: 6
}, "*");
}
window.onmessage = function(e) {
console.log(e.data); // 11
document.body.removeChild(iframe);
}