サーバサイドからクライアントサイドのJavaScriptを呼び出す際のベストプラクティス
JavaScript文字列のエスケープ – yohgaki's blog に対して、
最近だと id="hoge" data-foo="<% bar %>" しておいて $("#hoge").data('foo') でとりだすのが主流かと思います。
はてなブックマーク - JavaScript文字列のエスケープ – yohgaki's blog
のように、
- そもそもJavaScriptコードを動的生成すべきでない
- JavaScriptコードに渡す変数はHTMLノードを経由すべきだ
というような反論がついています。
が、はたしてそうでしょうか。
僕には、元の記事の手法も、HTMLノードを経由する手法もあまり好ましくない*1ように思えます。
そもそも、HTML生成時にXSS脆弱性が発生しがちなのは、
- タグや静的な文字列と動的に生成される文字列が混在し
- 埋め込まれる多数の文字列を正しくエスケープしなければならない
という事情ゆえです。これは、HTMLが人間が閲覧しやすいように生成されるものである以上、仕方がない制限です。
ですが、サーバサイドのプログラムからクライアントサイドのJavaScriptに情報を渡す場合には、この制限を気にする必要はありません。ですから、「データの受け渡し箇所を局所化する」ことで脆弱性の発生する可能性を低めるべきだと思います。
具体的には、どうすべきか。
ひとことでいうと、JSONPと同形式の呼出をサーバサイドで生成しSCRIPTタグとして埋め込む、という手法を採るべきだと思います。
実例をあげると、以下のような感じでしょうか。
<script> function doit(argsAsJson) { ... } </script> <script> doIt({ a: 123, b: "hello" }); // このステートメントを動的生成する </script>
動的生成するコードはJSONPの場合とほぼ同じ。以下の手順で生成できます。
この方式のメリットは以下のとおりになります。
- HTML文書内に、意味不明な情報が混ざらない
- クライアントサイドのプログラムとデータが明確に分離されるので、テストがしやすい
デメリットとしては、エンコード手法が HTML の場合と異なるという点がありますが、先に述べたように JavaScript へデータを渡す場合は処理を局所化できるので、上記のメリットと比べると些細なことかと思います。
注: 全面的に書き換えました。元は https://gist.github.com/kazuho/7329584
*1:JavaScriptに渡すべき情報が増えるたびにHTMLテンプレートの修正が必要になるとか