trust(html)
描述
把 HTML 字符串转换为未经转义的 HTML。不要对未经过滤的用户输入数据使用 m.trust
。
在考虑使用 m.trust
之前,优先考虑使用替代方法。
签名
vnode = m.trust(html)
参数 | 类型 | 是否必须 | 描述 |
---|---|---|---|
html | String | 是 | HTML 字符串 |
返回 | Vnode | 一个可信的 HTML 字符串生成的 vnode |
工作原理
Mithirl 会默认转义所有值,以防止 XSS 注入。
var userContent = "<script>alert('evil')</script>"
var view = m("div", userContent)
m.render(document.body, view)
// 等效 HTML
// <div><script>alert('evil')</script></div>
但是,有时候需要渲染富文本和格式化标签。为了实现这一需求,可以使用 m.trust
来创建可信任的 HTML vnode。
var view = m("div", [
m.trust("<h1>Here's some <em>HTML</em></h1>")
])
m.render(document.body, view)
// 等效 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 = {}
// 易受攻击的 HTML 字符串
var description = "<img alt='" + data.title + "'> <span>" + data.description + "</span>"
// 使用 onload 属性进行攻击
data.description = "<img onload='alert(1)'>"
// 使用不配对的引号进行攻击
data.description = "</span><img onload='alert(1)'><span"
// 使用不配对的引号进行攻击
data.title = "' onerror='alert(1)"
// 使用另一个属性进行攻击
data.title = "' onmouseover='alert(1)"
// 不使用 JavaScript 进行的攻击
data.description = "<a href='http://evil.com/login-page-that-steals-passwords.html'>Click here to read more</a>"
有无数种创建恶意代码的方式,因此强烈建议你使用白名单来过滤 HTML 标签、属性、以及属性值,并使用黑名单来过滤用户输入。同时强烈建议你使用标准的 HTML 解析器,而不是用正则表达式进行清理,因为正则表达式对边缘情况难以测试。
不运行的脚本
尽管有很多方法来使 HTML 字符串运行 JavaScript,但 <script>
标签出现在 HTML 字符串中时,就不会运行。
由于历史原因,浏览器会忽略通过 innerHTML 插入到 DOM 中的 <script>
标签。这样做是因为,一旦元素渲染完成(具有可访问的 innerHTML 属性),渲染引擎时不能退回到解析阶段的,如果脚本调用类似于 document.write("")
的代码的话,无法重新渲染。
这种浏览器行为对于 jQuery 开发者可能会感到惊讶,因为 jQuery 实现了专门查找 script 标签,并执行代码。Mithril 遵守浏览器行为。如果需要 jQuery 的行为,你可以把代码从 HTML 字符串移动到 oncreate
生命周期方法中,或者直接使用 jQuery。
避免信任 HTML
应尽量避免使用 m.trust
。除非你需要显示富文本,且没有其他更好的办法来获取所需结果。
// 避免这种用法
m("div", m.trust("hello world"))
// 推荐这种用法
m("div", "hello world")
避免盲目的复制和粘贴
一种常见的滥用 m.trust
的方式是使用第三方服务时,直接从第三方服务的教程中复制和粘贴 HTML 代码。在多数情况下,应该使用 vnode 来编写 HTML(通常通过 m() 函数来生成)
<!-- 加载 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="http://www.your-domain.com/your-page.html"
data-layout="standard"
data-action="like"
data-show-faces="true">
</div>
这是一种不使用 m.trust
创建 Mithril 组件的方式:
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=http://www.your-domain.com/your-page.html][data-layout=standard][data-action=like][data-show-faces=true]")
]
}
}
以上 Mithril 组件把 script 标签中的代码复制到了 oncreate
钩子中,并用 m() 语法声明了 HTML 标签。
避免使用 HTML 实体
另一种常见的滥用 m.trust
的方式是将其用于 HTML 实体。你应该使用对应的 unicode 代替:
// 避免这种用法
m("h1", "Coca-Cola", m.trust("™"))
// 建议这种用法
m("h1", "Coca-Cola™")
可以表示为 HTML 实体的所有字符都有对应的 unicode,包括不可见的字符,如
和
。
为了避免编码问题,应该把 JavaScript 文件的编码设置为 UTF-8,并在 HTML 文件中添加 <meta charset="utf-8">