【问题标题】:Are ES6 template literals safer than eval?ES6 模板文字是否比 eval 更安全?
【发布时间】:2016-09-11 15:29:02
【问题描述】:

对我来说,模板字面量有点像 eval,而且经常引用 using eval is a bad idea

我不关心performance of template literals,但我关心注入攻击(以及我可能没有想到的其他安全问题)。

编辑

An example of something that feels odd to me

let ii = 1;
function counter() {
    return ii++;
}
console.log(`${counter()}, ${ii++}, ${counter()}`);

哪些输出

1、2、3

模板字面量在全局层面产生副作用。既可以通过函数,也可以直接。

编辑 2

An example indicating the saferness of template literals

let ii = 1;
let inc = function() { ii++; }
console.log('Starting: ' + ii);
let input = prompt('Input something evil (suggestion: inc() or ii++)');
console.log(`You input: ${input}`);
console.log('After template literal: ' + ii);
eval(input);
console.log('After eval: ' + ii);

如果你在提示时输入ii++,它会记录

开始:1

你输入:ii+=1

模板文字之后:1

评估后:2

编辑 3

我已经开始研究 ECMAScript 规范

虽然我没有深入了解细节,但感觉模板文字的指定比 eval 更安全。

【问题讨论】:

  • 要问这样的问题,你必须首先了解使用 eval 的危险。动态危险概念在其他攻击如sql注入中也是相同的。我在这里要说的是,概念是一样的。这是你如何使用它的问题。
  • 您真正关心的是什么?模板字面量比eval() 更严格,因为它们(通常)所做的只是评估表达式并将结果强制转换为字符串。
  • 我一直避免使用 eval,而且我刚接触 ES6,所以我不确定从哪里开始真正了解模板文字的潜在危险/陷阱。它们的预期用途感觉更安全,但在评估模板文字与 eval 的方式上是否存在实际限制?
  • 模板文字在概念上比其他任何东西都更接近字符串连接。是什么让您认为它们与 eval 相关?你当然可以从我想的模板和 eval 构造一个字符串。
  • eval 将任意字符串作为代码运行。我不认为你可以用模板文字做到这一点。它们是完全不同的东西。

标签: javascript ecmascript-6 eval template-literals


【解决方案1】:

eval 的一个区别是模板文字在编译时解析,而eval 的参数仅在运行时解析,当eval 执行时。

与此相关的是eval 可以获取动态构建的参数,而模板文字是... 文字:它不能存储为模板变量,您可以动态构建、移动并最终解析:没有“模板变量”数据类型。一个标签函数实际上并不获取模板变量作为参数,而是它的解析组件,这些组件在编译时是已知的。

一些例子

使用eval你可以有这种情况:

var code = prompt('enter some evil code');
eval(code);

但是模板文字不可能做到这一点:

var literal = prompt('enter some evil template literal');
tag literal; // there is no data type or syntax for this.
`${literal}`; // and this just gives you the entered string.

什么是可能的,是这样的:

var str = prompt('enter some string');
tag`${str}`;

但这不会导致不需要的代码执行,至少不会比这更糟:

var str = prompt('enter some string');
myfunc(str);

任何函数调用都必须已经在模板字面量中进行了字面编码。字符串变量的值不能改变它。模板文字无法调用变量函数。这个:

`${func(str)}`;

...将调用func,并且仅调用该函数。它由程序员选择。

一个相当邪恶的模板文字

话虽如此,这仍然是可能的:

var func = prompt ("enter some evil function name (suggestion: 'alert')");
var param = prompt ("now provide an argument for " + func);

`${window[func](param)}`;

但很明显,程序自愿打开了在全局对象上执行任何函数的可能性。那么确实,你正在接近eval 的邪恶。

请注意,使用以下命令可以达到相同的效果:

window[name](param);

最邪恶的模板字面量

正如评论的那样,那么您不妨将这个模板文字化:

`eval(str)`;

...所以邪恶的部分并不在于模板文字,而是您设计在其中的通用函数调用。为此,您不需要模板文字或 eval,而是一个糟糕的程序员;-)

关于例子

你举了这个例子:

let ii = 1;
function counter() {
    return ii++;
}
console.log(`${counter()}, ${ii++}, ${counter()}`);

这将执行您的counter 函数,但与eval 的不同之处在于字符串文字在设计时已经存在,并且无法在运行时构造。此代码设计是为了增加您的计数器,与以下代码没有本质区别:

console.log(counter() + ', ' + (ii++) + ', ' + counter());

编译时间

为了强调编译/运行时解析的区别,请注意不能使用没有有效语法的模板文字运行代码。

比较这两个脚本:

alert('press OK');
eval('alert("hello)');

和:

alert('press OK');
`${alert("hello)}`;

注意语法错误。第一个脚本只会在解析eval的参数时在运行时注意到语法错误,而第二个脚本甚至不会运行,并立即给出语法错误。

更准确地说,eval 执行一个新脚本,具有自己的编译和运行阶段。模板文字像其他代码一样被解析/编译。

【讨论】:

  • 喜欢最邪恶的模板文字 :)
  • “最邪恶的模板文字”当然应该建议eval
  • @trincot - 感谢您在这里所做的所有努力!编译与运行时间对我来说很重要。我已经编辑了我的问题,深入挖掘,但我已经对模板文字感觉更好:)
【解决方案2】:

我认为 evaltemplate literals 之间有一个很大的区别。

eval 可以计算在代码中不直接可见的动态表达式。这很危险,因为您可以评估任何可能出现的字符串 从任何地方:客户端/第三方/数据库...

但是对于模板文字,情况有所不同,

  • 因为你可以从代码中看到完整的模板,
  • 表达式适用于您的内部对象,无法计算动态表达式。

例如,这将适用于 eval

function doSomething() {
  console.log('HELLO!');
}
    
// This will work.
var expression = 'doSomething()';
eval(expression);
  

但这不适用于模板文字

// This will not.
`${expression}`;

您需要静态插入表达式以使其工作

// To make it work you need to insert it statically.
`${doSomething()}`;

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

【讨论】:

    【解决方案3】:

    如果您担心的话,模板文字会自动转义引号。它们也不评估或执行任何东西,它们将您输入的任何内容转换为字符串。如果您担心 SQL 注入,请尝试使用模板文字执行此操作,您会看到它们被转义。

    您应该避免使用 eval,除非您有 非常好的理由 使用它并且您确实知道您需要它来实现您的目标。否则最好避免

    【讨论】:

    猜你喜欢
    • 2015-05-17
    • 2013-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 1970-01-01
    • 2017-12-06
    相关资源
    最近更新 更多