我在服务器中安全地使用 eval() 吗?

Am i using eval() safely in server?

简而言之,我正在使用 eval 动态调用用于验证客户端饼干。


const cookie_names = ['cookiename_1', 'cookiename_2'];

exports.validateCookies = (req, res, next) => {
    const cookie_types = filterUnknownCookies(Object.keys(req.cookies));
    for(let cookie of cookie_types){
        res.locals[cookie] = eval(`validateCookie${cookie}('${req.cookies[cookie]}')`);
    }
    next();
}

const filterUnknownCookies = (cookie_arr) => {
    cookie_arr = cookie_arr.filter(name =>  cookie_names.findIndex( validnames => validnames === name) + 1);

    return cookie_arr;
}


因为我知道使用 eval 函数可能很危险,所以我对有效的 cookie 名称进行了硬编码,并过滤掉数组中与它们不匹配的任何内容。 所以问题是:从任何不需要的字符串(名称)中过滤数组是否足够安全以抵消 eval 的可利用性? 提前致谢

So the question: is filtering the array from any unwanted strings (names) safe enough to counter the eval's exploitability?

没有。这不安全。事实上,它基本上对您没有任何保护作用,因为您允许的 cookie 名称仍然完全不受保护和未经处理。您拥有的东西可能不安全,因为恶意客户端可以在该 cookie 中放入他们想要的任何东西,而您“希望”他们找不到会破坏您拥有的字符串定界符的东西。但是,可以通过终止字符串然后添加函数调用来打破该字符串定界符。这可能会允许攻击者在您的服务器上执行任意代码。

您唯一应该使用 eval() 的是来自您自己的服务器端代码的可信字符串或来自外部的完全净化的字符串。但是,几乎总是不需要 eval(),因为有另一种更安全的编码方式。

在这里,你根本不需要使用eval()。您可以只为要调用的合法函数创建一个查找 table,然后将函数直接传递给它:

try {
    res.locals[cookie] = validateCookie[cookie](req.cookies[cookie]);
} catch(e) {
    // either invalid cookie or exception in the function
    // handle that here
}

当然,您的 validateCookie[cookie]() 函数也必须进行防御性编码,以了解它可能会传递任何内容。您没有向我们展示该功能的代码以便我们进一步评论。

在这种情况下,validateCookie 是一个包含有效 cookie 名称及其对应函数的查找 table:

 // cookie processing lookup table
 const validateCookie = {
      cookieName1: validateCookieName1,
      cookieName2: validateCookieName2
 };

像这样查找 table 通常是您避免尝试制造函数名称和字符串并使用 eval() 调用它的方法。这也增加了安全功能,即此代码无法调用任何不在您的查找中的函数 table.

不,代码不安全,不是因为它没有清理验证方法的名称,而是因为它在扩展字符串模板文字时将上传的文本评估为 JavaScript:

 `... ${req.cookies[cookie]} ...`

我刚刚使用 req.cookies[cookie] 的精心制作的字符串值测试并注入了代码,我将避免在此处发布。

在不扩展数据字符串的情况下评估验证调用可能稍微安全一些,如

`validateCookie${cookie}` + "(req.cookies[cookie])"

这会将上传的文本传递给验证例程而不作为代码进行评估,但避免 eval 的安全性完全保持不变。通过使用以函数名称为键的验证函数对象值的查找 table,可以轻松避免 eval