Are ES6 template literals safer than eval?
模板文字对我来说有点像eval,并且经常被引用使用eval是个坏主意。
我不关心模板文字的性能,但是我关心注入攻击(以及其他我可能没有想到的安全问题)。
编辑
一个让我感到奇怪的例子
1 2 3 4 5 | let ii = 1; function counter() { return ii++; } console.log(`${counter()}, ${ii++}, ${counter()}`); |
哪个输出
1, 2, 3
模板文字在全局级别产生副作用。既可以通过函数直接调用。
编辑2
表明模板文字更安全的示例
1 2 3 4 5 6 7 8 | 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); |
如果在提示时输入
Starting: 1
You input: ii+=1
After template literal: 1
After eval: 2
编辑3
我已经开始研究ECMAScript规范
-
18.2.1-全局对象的函数属性:: Eval(x)
- 注意它在全局对象上
- 注意,下一部分是关于eval的"运行时语义"
-
12.2.9-主表达式::模板文字
- 注意这是一个表达
- 注意下一部分是关于模板字符串的"静态语义"
尽管我不担心细节,但感觉模板字面量比eval更安全。
与
与此相关的是
一些例子
使用
1 2 | var code = prompt('enter some evil code'); eval(code); |
但是用模板文字是不可能的:
1 2 3 | 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. |
这是可能的:
1 2 | var str = prompt('enter some string'); tag`${str}`; |
但这不会导致不必要的代码执行,至少不会比这更糟:
1 2 | var str = prompt('enter some string'); myfunc(str); |
任何函数调用必须已经按原样编码在模板文本中。字符串变量的值不能改变它。模板文字无法调用变量函数。这:
1 | `${func(str)}`; |
...将调用
一个相当邪恶的模板文字
话虽如此,这仍然是可能的:
1 2 3 4 | var func = prompt ("enter some evil function name (suggestion: 'alert')"); var param = prompt ("now provide an argument for" + func); `${window[func](param)}`; |
但是很明显,该程序愿意打开在全局对象上执行任何功能的可能性。那么确实,您正在接近
请注意,使用以下方法可获得相同的效果:
1 | window[name](param); |
最邪恶的模板字面量
如前所述,那么您最好将这个模板文字设为:
1 | `eval(str)`; |
...因此,邪恶的部分不在模板文字中,而是您设计为包含在其中的泛型函数调用。为此,您不需要模板字面量或
关于例子
您给出了以下示例:
1 2 3 4 5 | let ii = 1; function counter() { return ii++; } console.log(`${counter()}, ${ii++}, ${counter()}`); |
这将执行您的
1 | console.log(counter() + ', ' + (ii++) + ', ' + counter()); |
编译时间
为了强调编译/运行时解析的区别,请注意,您不能使用没有有效语法的模板文字来运行代码。
比较这两个脚本:
1 2 | alert('press OK'); eval('alert("hello)'); |
和:
1 2 | alert('press OK'); `${alert("hello)}`; |
请注意语法错误。当解析
更确切地说,
我认为eval和模板文字之间有一个很大的区别。
eval可以评估在代码中不直接可见的动态表达式。这很危险,因为您可以评估可能出现的任何字符串
来自任何地方:客户端/第三方/数据库...
但是对于模板文字而言,情况则有所不同,
- 因为您可以从代码中看到完整的模板,
- 表达式可与您的内部对象一起使用,无法评估动态表达式。
例如,这将与eval
一起使用
1 2 3 4 5 6 7 | function doSomething() { console.log('HELLO!'); } // This will work. var expression = 'doSomething()'; eval(expression); |
但这不适用于模板文字
1 2 | // This will not. `${expression}`; |
您需要静态插入表达式以使其起作用
1 2 | // To make it work you need to insert it statically. `${doSomething()}`; |
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Template_literals
如果您担心的是模板文字会自动转义引号。它们也不评估或执行任何操作,而是将您输入的任何内容转换为字符串。如果您担心SQL注入,请尝试使用模板文字进行操作,您会发现它们被转义了。
您应该避免使用eval,除非您有充分的理由使用它,并且您确实知道需要eval才能实现目标。否则,最好避免。