ES6模板字面量比eval更安全吗?

25

对我来说,模板文字的味道有点像eval函数,而且经常被引用到使用eval是个坏主意

我不关心模板文字的性能,但我担心注入攻击(以及其他我可能没有考虑到的安全问题)。

编辑

这是一个让我感觉奇怪的例子

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

输出为

1、2、3

模板字面量在全局级别上通过函数和直接方式产生副作用。

编辑2

一个示例,说明模板字面量的安全性

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

eval后:2


编辑3

我已开始研究ECMAScript规范。

尽管我还没有完全理解细节,但模板字面量似乎比eval规范得更为安全。


1
你到底关心的是什么?模板字符串比eval()严格得多,因为它们通常只评估表达式并将结果强制转换为字符串。 - Pointy
1
模板字面量在概念上更接近于字符串拼接而不是其他任何东西。你为什么认为它们与eval相关呢?我想你可以从模板构造一个字符串,然后进行eval操作。 - loganfsmyth
eval函数可以执行任意字符串作为代码。我认为你不能用模板文字来实现这个功能,因为它们是完全不同的东西。 - Oriol
几乎任何东西都比eval更安全... - Leonid Beschastny
将您的示例与 console.log(counter() + ', ' + ii++ + ', ' + counter()) 进行比较,这是完全等效的。您仍然认为它不安全吗? - Felix Kling
显示剩余2条评论
3个回答

36

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执行一个新的脚本,具有自己的编译和运行阶段。模板字面量与其他代码一样被解析/编译。


4
最喜欢邪恶的模板字面量 :) - gevorg
1
“最邪恶的模板字面量”应该指的是 eval - Oriol
@trincot - 感谢您在这里的所有努力!编译时与运行时对我来说是一个重要的点。我已经深入挖掘了我的问题,但我已经对模板字面量感到更好了 :) - Anthony Astige

2
我认为eval模板字面量之间有一个重要的区别。 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


-1

如果你担心引号的问题,那么模板文字会自动转义引号。它们也不会执行或评估任何内容,而是将您输入的任何东西转换为字符串。如果您担心 SQL 注入,请尝试使用模板文字,并且您会发现它们被转义了。

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


SQL注入和模板字面量:https://dev59.com/yKHia4cB1Zd3GeqPa-gT - jkdev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接