Mithril trust(html)

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>&lt;script&gt;alert('evil')&lt;/script&gt;</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> 标签上添加 onloadonerror 属性,并使用不配对的引号(例如 " 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("&trade;"))

// 建议这种用法
m("h1", "Coca-Cola™")

可以表示为 HTML 实体的所有字符都有对应的 unicode,包括不可见的字符,如  ­

为了避免编码问题,应该把 JavaScript 文件的编码设置为 UTF-8,并在 HTML 文件中添加 <meta charset="utf-8">