使用 `Function` 构造函数来验证 JavaScript 语法是否安全?

问题描述 投票:0回答:2

我想验证(客户端)用户是否输入了有效的 JavaScript 代码。

引入 Javascript 解析器(例如 Acorn 或 Esprima)是一个相对较重的依赖项。但是(如果未启用 CSP),我可以这样做:

try {
    new Function(userCode);
    return {valid: true}
} catch (e) {
    return {
        valid: false,
        error: e.message
    };
}

虽然这确实使用了

new Function
,但用户提供的代码永远不会 run,而只会被解析/编译。还存在安全问题吗?

javascript security eval client-side
2个回答
2
投票

这看起来不错。解析/编译 JS 代码是安全的,没有副作用(模块解析和加载不适用于

Function
)。我们可以肯定,现代浏览器中使用的 JavaScript 解析器几乎没有错误。


1
投票

该标准(甚至早于 ECMA-262 第一版,§15.3.2.1,以及所有后续标准)指定调用

Function
构造函数将仅解析和编译给定的代码,而不是执行它;所以在纸面上,在现代主流浏览器引擎中,这应该“可能”没问题。但即使在相对较新的版本(2016 年最新版本)下,这实际上也非常不安全。一些引擎曾经将 Function 构造函数实现为道德上的等价物:
function Function() {
  var args = Array.prototype.slice.call(arguments, 0, -1).join(",");
  var body = arguments[arguments.length - 1];
  return (0, eval)("(function anonymous(" + args + "\n) {\n" + body + "\n});");
}

all

的后果 - 特别是,在 V8 下,您可以调用类似 new Function("}, alert(1), function () {") 的东西并让它调用任意代码:请参阅

bug #2470
。同时,当向构造函数提供无效代码时,JavaScriptCore 可能会彻底崩溃:请参阅bugs #106160#163748。即使在今天,不太主流的引擎也可能倾向于走同样的捷径。例如,QuickJS 就是这样做的,并且从 2024 年起仍然容易受到此欺骗的影响:https://github.com/bellard/quickjs/blob/06651314f5bbef1bb7a2689da7fd7b2b04ce6125/quickjs.c#L38342. 为了防止这种草率的实现,您可能需要检查

Function

构造函数中是否存在已知错误,并在检测到任何错误时禁用客户端检查。

function isSyntaxCheckingSafe() { function check() { try { Function.apply(null, arguments); return true; } catch (e) { if (!(e instanceof SyntaxError)) return true; } return false; } /* this one first, as it does not outright crash buggy JavaScriptCore */ if (check("/*", "*/){")) return false; if (check("},function(){")) return false; if (check("};{")) return false; /* no problems found, fingers crossed */ return true; } console.log(isSyntaxCheckingSafe());

上述测试很容易产生误报,因为它们会判断引擎成功解析格式错误的

Function

参数,但实际上并不执行任何易受攻击的注入代码。但安全总比后悔好。

    

© www.soinside.com 2019 - 2024. All rights reserved.