在 JavaScript 程序中重构动态加载的脚本以避免 eval

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

这可能是一个特定的问题(研究项目),但我正在寻找一种使该程序比目前明显“更安全”的好方法。

目标是接受任意用户代码(在通过单元测试和人工检查后)并将其合并到正在运行的程序中,而无需重新加载应用程序。

具体的例子是它是一个持续执行的绘图应用程序,我希望学生/其他用户能够提交一个脚本(遵循特定的格式/指南)。

我目前的实现是要求用户指定他们的类名匹配他们的文件名(例如,

myclass.js
里面有一个
const myclass = class { ... }
块)。然后当我看到我的服务器中存在一个新文件时(一个检查特定目录内容的 Flask 应用程序),JavaScript 应用程序将加载该特定文件。

目前我有一个

promise
来确保脚本被加载,但我正在做的一般方法是将它带入内存:

function loadNewScript(scriptName) {
  let s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", `/static/techniques/${scriptName}`);
  let nodes = document.getElementsByTagName("*");
  let node = nodes[nodes.length - 1].parentNode;
  node.appendChild(s);
}

但是,我预期的安全问题(除了我想的预期任意代码)是我目前正在使用

eval
将类带入内存:

// technique comes in as 'myclass.js'
function loadObject(technique) {
    try {
        let obj = eval(technique.split(".")[0]);
        let _activeObj = new obj();
        if (typeof _activeObj != "undefined") {
            return _activeObj;
        }
        return null;
    } 
    catch (e) { // will check for syntax errors, but this usually trips when the script isn't loaded yet
        return null;
    }
}

目前这可行,但是我有点担心在这里使用

eval
。我看过有关使用
window
this
命名空间的帖子,但据我了解,您不能将动态加载的脚本添加到全局命名空间中吗?无论如何,使用
window['myclass']
this['myclass']
在加载后不起作用。 (当我将其名称作为字符串时如何执行 JavaScript 函数

javascript eval
1个回答
1
投票

实际上,如果您已经将“任意”(审查过的)代码加载到动态

eval
中,那么为了安全而避免
<script>
毫无意义。
window['myclass']
不起作用的问题是因为
const
确实声明了全局变量但创建了全局对象的属性
。您需要更改脚本以改为使用
var myclass = class { ... };

但是,我建议您更改实现以要求文件遵循 ES6 模块格式:

export default class myclass { ... }

然后你可以加载这些使用

function loadNewScript(scriptName) {
    return import(`/static/techniques/${scriptName}`);
}
async function loadObject(technique) {
    const module = await loadNewScript(technique);
    const obj = module.default;
    return new obj();
}

如果你需要

loadObject
方法是同步的(而不是返回一个promise),你可以使用对象或者
Map
作为类注册

const registry = {};
async function loadNewScript(scriptName) {
    const module = await import(`/static/techniques/${scriptName}`);
    registry[scriptName] = module.default;
}
function loadObject(technique) {
    const obj = registry[technique];
    return obj ? new obj() : null;
}
© www.soinside.com 2019 - 2024. All rights reserved.