Mithril 1.1.0

trust(html)


説明

HTML文字列をエスケープしないHTMLとして出力します。m.trustはサニタイズしていないユーザー入力に対して使わないでください。

m.trustの使用を検討するまえに他の手段で代替できないかまず検討してください。


シグニチャ

vnode = m.trust(html)

引数 必須 説明
html String Yes HTMLテキストを含む文字列
返り値 Vnode 入力文字列を表現するための、信用できるHTMLvnode

シグニチャの読み方


どのように動作するのか

デフォルトでは、Mithrilは、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>

しかし、すでにレンダリングされたリッチテキストや、マークアップでフォーマットが整えられたHTMLを表示したいこともあります。m.trustは、入力されたHTMLと等価な信用されたvnodesを出力します。

var view = m("div", [
    m.trust("<h1>ここに生の<em>HTML</em>が書けます</h1>")
])

m.render(document.body, view)

// 等価なHTML
// <div><h1>ここに生の<em>HTML</em>が書けます</h1></div>

出力の信用済みHTMLのvnodeはオブジェクトであって文字列でないため、通常の文字列と接合することはできません。


セキュリティの懸念

m.trust入力は必ずサニタイズしましょう。ユーザーが生成した悪意を持ったコードがHTMLに入り込まないようにしましょう。HTML文字列をサニタイズしないまま、信頼できる文字列としてマークてしまうと、ページを表示しているユーザーの権限で、JavaScriptを使って任意の非同期呼び出しが可能となってしまいます。

HTMLには、実行可能なコードを書くことができるポイントがたくさんあります。一般的なXSS攻撃では、<img>タグや<iframe>タグのonloadonerror属性を使ったり、" onerror="alert(1)といった不揃いのダブルクオートを使って攻撃コードをもぐりこませます。

var data = {}

// 脆弱なHTML文字列
var description = "<img alt='" + data.title + "'> <span>" + data.description + "</span>"

// JavaScriptが書ける属性を使った攻撃
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文字列にJavascriptを実行させるためのあいまいな方法はたくさんありますが、<script>タグはHTML文字列に現れるときには実行されません。

歴史的な理由により、ブラウザはinnerHTMLを使ってDOMに挿入された<script>タグを無視します。一度エレメントが準備可能状態になり、innerHTMLプロパティなどが利用できるようになったら、document.write("")といった呼び出しをしても、レンダリングエンジンがパースのステージにもどることはありません。

jQueryがスクリプトタグを特定してこのシナリオでも実行されるような実装になっているため、jQueryの経験者はこのブラウザの動作を見て驚かれるかもしれません。Mithrilはブラウザの動作の方に従います。もしjQueryの方の挙動が望ましいのであれば、HTML文字列からコードを除外してoncreateライフサイクルメソッドに移動するか、jQueryを使用するか、スクリプトのパースコードを自作する必要があります。


HTMLを信用しない

一般的なルールとして、明示的にレンダリングされたHTMLを使い、なおかつ他に自分が望む手法が一切無い時以外はm.trustは使わないようにしましょう。

// 避けるべきコード
m("div", m.trust("hello world"))

// 望ましいコード
m("div", "hello world")

盲目的なコピー&ペーストを避ける

m.trustの良くある誤用は、サードパーティーのサービスのチュートリアルで、コードを含むHTMLコードが表示されていて、それをコピー&ペーストしてしまうことです。ほとんどの場合、HTMLはvnode(m()ユーティリティ)を使って書かなければなりません。

これがFacebookのいいねボタンのサンプルです:

<!-- Facebook SDK for JavaScriptのロード -->
<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フックにコピーして、HTMLタグはMithrilのm()構文で表現しています。

HTML文字参照を避ける

他のよくあるm.trustの誤用はHTML文字参照です。望ましい方法はUnicode文字を使う方法です:

// 避けるべきコード
m("h1", "Coca-Cola", m.trust("&trade;"))

// 望ましいコード
m("h1", "Coca-Cola™")

アクセント文字はキーボードを使って入力することができます。また、キーボードのショートカットを覚えて入力することもできます(™文字はWindowsではAlt+0153、macではOption+2で入力できる)。他のシンプルな方法は、Unicode文字列テーブルからコピー&ペーストする方法です。他には、Unicodeのコードポイントを使って入力する方法(™シンボルは"\u2122")もあります。

&nbsp;&shy;といった非表示のものまで含めて、すべてのHTML文字参照には、対応するUnicode文字が存在します。

エンコーディングの問題を避けるためには、JavaScriptのファイルのエンコーディングをUTF-8にしたり、ホストとなるHTMLファイルに<meta charset="utf-8">メタタグを追加する必要があります。


License: MIT. © Leo Horie.