在某些时候,您必须信任窗口上可用的内容。这意味着缓存您计划使用的函数,或尝试沙盒化您的代码。
调用call
的“简单”解决方案是暂时设置属性:
var safeCall = (function (call, id) {
return function (fn, ctx) {
var ret,
args,
i;
args = [];
for (i = 2; i < arguments.length; i++) {
args.push(arguments[i]);
}
call[id] = call;
ret = call[id](fn, ctx, args);
delete call[id];
return ret;
};
}(Function.prototype.call, (''+Math.random()).slice(2)));
这可以被用作:
safeCall(fn, ctx, ...params);
请注意,传递给safeCall的参数将被合并到一个数组中。您需要使用apply
来正确处理它,而我只是在尝试简化依赖关系。
改进版的safeCall
添加了对apply
的依赖:
var safeCall = (function (call, apply, id) {
return function (fn, ctx) {
var ret,
args,
i;
args = [];
for (i = 2; i < arguments.length; i++) {
args.push(arguments[i]);
}
apply[id] = call;
ret = apply[id](fn, ctx, args);
delete apply[id];
return ret;
};
}(Function.prototype.call, Function.prototype.apply, (''+Math.random()).slice(2)));
这可以用作:
这可以用作:
safeCall(fn, ctx, ...params);
安全调用call的另一种解决方案是使用来自不同窗口上下文的函数。
这可以通过创建一个新的iframe
并从其窗口中获取函数来简单地完成。您仍需要假设某些DOM操作函数可用,但这是设置步骤之一,因此任何将来的更改都不会影响现有脚本:
var sandboxCall = (function () {
var sandbox,
call;
// create a sandbox to play in
sandbox = document.createElement('iframe');
sandbox.src = 'about:blank';
document.body.appendChild(sandbox);
// grab the function you need from the sandbox
call = sandbox.contentWindow.Function.prototype.call;
// dump the sandbox
document.body.removeChild(sandbox);
return call;
}());
这可以用作以下内容:
sandboxCall.call(fn, ctx, ...params);
safeCall
和sandboxCall
都可以安全地避免对Function.prototype.call
的未来更改,但是您可以看到它们依赖于一些现有的全局函数在运行时工作。如果恶意脚本在此代码之前执行,则您的代码仍然容易受到攻击。