trust(html)
Description
HTMLまたはSVG文字列を、エスケープせずにHTMLまたはSVGとして表示します。サニタイズされていないユーザー入力にm.trust
を絶対に使用しないでください。
m.trust
を使用する前に、常に代替手段を検討してください。
Signature
vnode = m.trust(html)
引数 | 型 | 必須 | 説明 |
---|---|---|---|
html | String | はい | HTMLまたはSVGテキストを含む文字列 |
戻り値 | Vnode | 入力文字列を表す信頼されたHTMLvnode |
How it works
デフォルトでは、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として表示される信頼済みHTMLvnodeを作成します。
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はオブジェクトであり、文字列ではないため、通常の文字列と連結することはできません。
Security considerations
m.trust
を使用する際は、HTML文字列に悪意のあるコードが含まれていないか必ず入力値をサニタイズしてください。HTML文字列をサニタイズせずに、信頼された文字列としてマークすると、HTML文字列内の非同期JavaScript呼び出しが実行され、ページを閲覧しているユーザーの権限で動作する可能性があります。
HTML文字列に実行可能コードを埋め込む方法は多数存在します。セキュリティ攻撃を仕込む一般的な方法としては、<img>
や<iframe>
タグにonload
やonerror
属性を付与したり、サニタイズされていない文字列の埋め込み箇所で" onerror="alert(1)
のように引用符の対応を崩してJavaScriptを実行させるなどが挙げられます。
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パーサーを使用することを強くお勧めします。
Scripts that do not run
HTML文字列でJavaScriptを実行する方法は多数ありますが、HTML文字列に<script>
タグが含まれていても、実行されないケースがあります。
過去の経緯から、ブラウザーはinnerHTML
を介してDOMに挿入された<script>
タグを無視します。これは、要素が準備完了し(innerHTML
プロパティがアクセス可能になった後)、スクリプトがdocument.write("</body>")
のようなものを呼び出した場合、レンダリングエンジンは解析段階に戻れないためです。
jQuery経験者には意外に思えるかもしれません。jQueryはこのシナリオでスクリプトタグを検出し、実行する特別なコードを実装しているからです。Mithril.jsはブラウザーの動作に従います。jQueryと同様の動作が必要な場合は、HTML文字列からコードを切り出し、oncreate
ライフサイクルメソッド内で実行するか、jQueryを使用する(または、jQueryのスクリプト解析コードを再実装する)ことを検討してください。
Avoid trusting HTML
原則として、リッチテキストを明示的に表示する必要があり、かつ他の方法で同様の結果が得られない場合にのみ、m.trust
の使用を検討してください。
// AVOID
m('div', m.trust('hello world'));
// PREFER
m('div', 'hello world');
Avoid blind copying and pasting
m.trust
が誤用されやすいケースの1つは、HTMLコードをコピー&ペーストするサードパーティサービスを利用する場合です。ほとんどの場合、HTMLは通常のvnode(m()
ユーティリティを使用)で記述すべきです。
Facebook Like buttonのサンプルコードは以下の通りです。
<!-- Load 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>
<!-- Your like button code -->
<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
フックに移動し、残りのHTMLタグはMithril.jsのm()
構文で記述するだけです。
Avoid HTML entities
m.trust
が誤用されやすいもう1つのケースはHTMLエンティティの使用です。より良い方法は対応するUnicode文字を使うことです。
// AVOID
m('h1', 'Coca-Cola', m.trust('™'));
// PREFER
m('h1', 'Coca-Cola™');
アクセント付き文字などのUnicode文字は、対応する言語のキーボードレイアウトを使用するか、よく使う記号であればキーボードショートカット(例:WindowsのAlt+0153
、MacのOption+2
で™記号)を覚えておくと便利です。あるいは、エスケープされたUnicodeコードポイント(例:™記号の場合は"\u2122"
)を直接入力する方法もあります。
HTMLエンティティで表現できる文字はすべて、
や­
といった不可視文字も含めて、Unicodeで表現できます。
文字エンコーディングの問題を避けるため、JavaScriptファイルのエンコードをUTF-8に設定し、ホストとなるHTMLファイルにも<meta charset="utf-8">
メタタグを追加してください。