trust(html) - Mithril.js
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><script>alert('evil')</script></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>
タグのonload
、onerror
属性を使ったり、" 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を使い、なおかつ他に自分が望む手法が一切無い時以外は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()
構文で表現しています。
他のよくあるm.trust
の誤用はHTML文字参照です。望ましい方法はUnicode文字を使う方法です:
// 避けるべきコード
m("h1", "Coca-Cola", m.trust("™"))
// 望ましいコード
m("h1", "Coca-Cola™")
アクセント文字はキーボードを使って入力することができます。また、キーボードのショートカットを覚えて入力することもできます(™文字はWindowsではAlt+0153
、macではOption+2
で入力できる)。他のシンプルな方法は、Unicode文字列テーブルからコピー&ペーストする方法です。他には、Unicodeのコードポイントを使って入力する方法(™シンボルは"\u2122"
)もあります。
や­
といった非表示のものまで含めて、すべてのHTML文字参照には、対応するUnicode文字が存在します。
エンコーディングの問題を避けるためには、JavaScriptのファイルのエンコーディングをUTF-8にしたり、ホストとなるHTMLファイルに<meta charset="utf-8">
メタタグを追加する必要があります。
License: MIT. © Leo Horie.