ES2015模板字符串安全问题
ES2015 template strings security issue
这是MDN的一句话:
模板字符串不得由不受信任的用户构造,因为他们有权访问变量和函数。
举个例子:
`${console.warn("this is",this)}`; // "this is" Window
let a = 10;
console.warn(`${a+=20}`); // "30"
console.warn(a); // 30
这里的例子没有显示我可以看到的任何漏洞。
谁能举一个利用这一点的漏洞利用的例子?
毫无意义。模板字符串无法访问任何内容,也不会执行。模板字符串是语言的语法元素。
因此,动态构造模板字符串没有问题 - 这就像构建表达式(无论格式如何,无论是代码字符串还是 AST)。MDN 暗示的问题是评估这样的表达式(例如使用 eval
,将其序列化为提供给用户的脚本等) - 它可能包含任意代码,而不是字符串文字!但你当然不会那样做,对吧?
此警告类似于说"使用 +
运算符的串联不得由不受信任的用户构造,因为他们有权访问变量和函数",并给出示例"" + console.warn("this is",this) + ""
。嗯,对于语言的任何表达都是如此,所以它不是特别有趣。
当我们谈论蹩脚的编码时,当然有一种情况是使用模板字符串(嘿,它们是多行的等等)而不是字符串文字可能会导致问题:
function escapeString(str) {
return JSON.stringify(str).slice(1, -1)
.replace(/'u2028/g, "''u2028").replace(/'u2029/g, "''u2029");
}
// This is (kinda) fine!
var statement = 'var x = "Hello,''n'+escapeString(userInput)+'";';
eval(statement); // some kind of evaluation
// But this is not:
var statement = 'var x = `Hello,'n'+escapeString(userInput)+'`;';
// ^ ^
现在想象一下userInput
包含一个${…}
- 我们没有逃脱......
@Bergi是正确的 - 这里的危险涉及使用eval
或类似的方法来允许用户构造实际的模板字符串,而不是替换。
示例漏洞利用:懒惰的开发人员希望允许用户在他们的评论中执行一些字符串替换,例如在像 SO 这样的网站上引用其他用户或问题。他没有为此开发令牌,然后进行适当的解析和替换,而是决定接受这样的语法:
"I think ${firstPoster} is an idiot! See ${question(1234)} for details!"
并通过这样的函数运行它:
var firstPoster = {...};
function question() {...}
processInput(input) {
return eval('`' + input + '`');
}
如果此代码在客户端上eval
并显示给其他用户,则恶意用户可能会注入 XSS 攻击。如果它在服务器上eval
,攻击者可以控制机器。
该示例似乎不再在 MDN 文档中。正如 Bergi 的回答所指出的那样,给定的示例似乎并没有突出模板字符串的任何特殊之处。
但是,在从对象构建字符串时,您绝对应该注意一个特殊的安全问题:
如果将 toString()
方法传递给字符串插值/串联表达式,则将在非字符串对象上隐式调用该方法。
可能还有其他隐式调用toString()
的情况。但在我看来,字符串插值是最常见的插值之一;事实上,这是我经常经历的。例如,假设您以某种方式从外部某个地方接收对象,例如通过iframe
到postMessage
。在这种情况下,您可能希望执行诸如将收到的消息记录到控制台之类的操作 - 并且您可能只想将对象直接传递到内插字符串中。
但是发送方(可能是攻击者)可以完全控制toString()
的定义,并且可以在其中插入他们喜欢的任何代码。因此,一旦您将该对象传递给内插或串联字符串,只要其他人控制所述对象的定义,您就容易受到攻击。
这里有一个简单的例子(添加到codesandbox中),表明即使对象在toString()
中返回看似无害的字符串,它们确实可以做一些危险的事情,比如读取本地存储:
import "./styles.css";
localStorage.setItem("secret1", "sssh! One");
localStorage.setItem("secret2", "sssh! Two");
const evilObject1 = {
toString() {
alert("I stole a secret: " + localStorage.getItem("secret1"))
return "I'm innocent";
}
};
const evilObject2 = {
toString() {
alert("I stole a secret: " + localStorage.getItem("secret2"))
return "I'm innocent";
}
};
const strInter = `Seemingly innocent object, interpolated: ${evilObject1}`;
const strConcat = "Seemingly innocent object, concatenated: " + evilObject2;
let p = document.createElement("p");
p.innerHTML = strInter
let p2 = document.createElement("p");
p2.innerHTML = strConcat
document.body.appendChild(p);
document.body.appendChild(p2);
对于鸭子类型的Javascript,这是一个非常真实的漏洞,因为您可能认为您收到的对象(例如通过postMessage
)是一个字符串,实际上,它的行为可能像字符串(因为它具有巧妙设计的toString()
方法),但除非您动态检查类型,否则您不知道是否真的得到了字符串。
如果需要字符串,可以按如下方式修复上述漏洞:
const sanitized1 = typeof evilObject1 === 'string' ? evilObject1 : "BAD OBJECT1"
const sanitized2 = typeof evilObject2 === 'string' ? evilObject2 : "BAD OBJECT2"
const strInter = `Seemingly innocent object, interpolated: ${sanitized1}`;
const strConcat = "Seemingly innocent object, concatenated: " + sanitized2;
通过此修复,可以避免在不安全对象上隐式调用toString()
。你得到的对象要么是字符串,这些将被使用,要么它们不是,你会得到"坏对象"文本。
- 关于ajax的安全问题
- AngularJs基于角色的菜单显示安全问题
- Http响应拆分安全问题的XMLHTTPRequest/Response用例的Filter实现
- 为什么我不能强制下载受污染的画布,为什么这是一个安全问题
- 让用户通过javascript访问cookie是一个安全问题
- 使用不受信任的字符串(require.js)调用require()会引发安全问题
- Openlayers IE安全问题
- 如何应对iframe安全问题
- 使用 javascript 创建用户帐户时存在 Firebase 安全问题
- 依赖于jquery和css的html标记中的安全问题
- XMPP Strophe.JS应用程序安全问题
- 在javascript中操纵信用卡号是否会导致安全问题
- Ajax 回调断点安全问题
- 安全问题是将我的访问者带到一个危险的下载页面
- Openlayer 3 安全问题
- 将 JavaScript 变量传递给 PHP 变量的安全问题
- 读取 JS 数组的安全问题
- 防止 iframe 安全问题
- ES2015模板字符串安全问题
- 关于chrome安全问题的Canvas to DataURI()