trust(html)
描述
将 HTML 或 SVG 字符串转换为未经转义的 HTML 或 SVG。切勿在未经清理的用户输入上使用 m.trust
。
在使用 m.trust
之前,请务必优先考虑使用替代方案。
签名
vnode = m.trust(html)
参数 | 类型 | 必需 | 描述 |
---|---|---|---|
html | String | 是 | 包含 HTML 或 SVG 文本的字符串 |
返回 | Vnode | 表示输入字符串的受信任的 HTML vnode |
工作原理
默认情况下,Mithril.js 会对所有值进行转义,以防止一类称为 XSS 注入 的安全问题。
var userContent = "<script>alert('evil')</script>";
var view = m('div', userContent);
m.render(document.body, view);
// equivalent HTML
// <div><script>alert('evil')</script></div>
但是,有时需要渲染富文本和格式化标记。为了满足这种需求,m.trust
会创建受信任的 HTML vnode,这些 vnode 会被渲染为 HTML。
var view = m('div', [m.trust("<h1>Here's some <em>HTML</em></h1>")]);
m.render(document.body, view);
// equivalent HTML
// <div><h1>Here's some <em>HTML</em></h1></div>
可信的 HTML vnode 是对象,而不是字符串;因此,它们不能与常规字符串连接。
安全注意事项
您必须对 m.trust
的输入进行清理,以确保 HTML 字符串中没有用户生成的恶意代码。如果您不对 HTML 字符串进行清理并将其标记为可信字符串,则 HTML 字符串中的任何异步 JavaScript 调用都将被触发,并以查看该页面的用户的授权级别运行。
HTML 字符串可能包含可执行代码的方式有很多。注入安全攻击最常见的方法是在 <img>
或 <iframe>
标签中添加 onload
或 onerror
属性,以及使用不平衡的引号(例如 " onerror="alert(1)
)在未经清理的字符串插值中注入可执行上下文。
var data = {};
// Sample vulnerable HTML string
var description =
"<img alt='" + data.title + "'> <span>" + data.description + '</span>';
// An attack using JavaScript-related attributes
data.description = "<img onload='alert(1)'>";
// An attack using unbalanced tags
data.description = "</span><img onload='alert(1)'><span";
// An attack using unbalanced quotes
data.title = "' onerror='alert(1)";
// An attack using a different attribute
data.title = "' onmouseover='alert(1)";
// An attack that does not use JavaScript
data.description =
"<a href='https://evil.com/login-page-that-steals-passwords.html'>Click here to read more</a>";
创建恶意代码的方法数不胜数,因此强烈建议您使用允许的 HTML 标签、属性和属性值的白名单,而不是黑名单来清理用户输入。还强烈建议您使用适当的 HTML 解析器,而不是使用正则表达式进行清理,因为正则表达式极难测试所有边界情况。
不会运行的脚本
尽管有许多复杂的方法可以使 HTML 字符串运行 JavaScript,但 HTML 字符串中的 <script>
标签通常不会运行。
出于历史原因,浏览器会忽略通过 innerHTML
插入到 DOM 中的 <script>
标签。 这样做的原因是,一旦元素准备就绪(因此具有可访问的 innerHTML
属性),如果脚本调用类似 document.write("</body>")
的内容,则渲染引擎无法回溯到解析阶段。
对于来自 jQuery 的开发人员来说,这种浏览器行为可能会让人感到意外,因为 jQuery 专门实现了代码来查找脚本标签并在此场景中运行它们。Mithril.js 遵循浏览器行为。如果需要 jQuery 行为,您应该考虑将代码移出 HTML 字符串并移入 oncreate
生命周期方法,或者使用 jQuery(或重新实现其脚本解析代码)。
避免信任 HTML
作为一般规则,除非您需要明确渲染富文本且没有其他方法可用,否则应避免使用 m.trust
。
// AVOID
m('div', m.trust('hello world'));
// PREFER
m('div', 'hello world');
避免盲目复制和粘贴
滥用 m.trust
的一种常见方式是在使用第三方服务时,这些服务的教程包含需要复制和粘贴的 HTML 代码。在大多数情况下,应使用 vnode 编写 HTML(通常通过 m()
实用程序)。
这是 Facebook Like 按钮 的示例代码段:
<!-- 加载 Facebook JavaScript SDK -->
<div id="fb-root"></div>
<script>
(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = '//connect.facebook.net/en_US/sdk.js#xfbml=1';
fjs.parentNode.insertBefore(js, fjs);
})(document, 'script', 'facebook-jssdk');
</script>
<!-- 点赞按钮 -->
<div
class="fb-like"
data-href="https://www.your-domain.com/your-page.html"
data-layout="standard"
data-action="like"
data-show-faces="true"
></div>
这是如何以避免 m.trust
的方式将其重构为 Mithril.js 组件:
var FacebookLikeButton = {
oncreate: function () {
(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = '//connect.facebook.net/en_US/sdk.js#xfbml=1';
fjs.parentNode.insertBefore(js, fjs);
})(document, 'script', 'facebook-jssdk');
},
view: function () {
return [
m('#fb-root'),
m(
'.fb-like[data-href=https://www.your-domain.com/your-page.html][data-layout=standard][data-action=like][data-show-faces=true]'
),
];
},
};
上面的 Mithril.js 组件只是将脚本标签的代码复制到 oncreate
钩子中,并使用 Mithril.js 的 m()
语法声明剩余的 HTML 标签。
避免 HTML 实体
滥用 m.trust
的一种常见方式是将其用于 HTML 实体。更好的做法是使用相应的 Unicode 字符:
// AVOID
m('h1', 'Coca-Cola', m.trust('™'));
// PREFER
m('h1', 'Coca-Cola™');
可以使用相应语言的键盘布局输入带重音的 Unicode 字符,也可以选择记住键盘快捷键来生成常用符号(例如,Windows 中的 Alt+0153
或 Mac 上的 Option+2
表示 ™ 符号)。另一种简单的方法是从 Unicode 字符表 中简单地复制和粘贴所需的字符。另一种相关方法是输入转义后的 Unicode 代码点(例如,"\u2122"
表示 ™ 符号)。
所有可以表示为 HTML 实体的字符都具有 Unicode 对应项,包括不可见字符,例如
和 ­
。
为避免编码问题,您应该将 JavaScript 文件的文件编码设置为 UTF-8,并在主 HTML 文件中添加 <meta charset="utf-8">
meta 标签。