PHPのイタい入門書を読んでAjaxのXSSについて検討した(1)
このエントリでは、あるPHPの入門書を題材として、Ajaxアプリケーションの脆弱性について検討します。全3回となる予定です。
このエントリを書いたきっかけ
twitterからタレコミをちょうだいして、作りながら基礎から学ぶPHPによるWebアプリケーション入門XAMPP/jQuery/HTML5で作るイマドキのWeという本を読みました。所感は以下の通りです。
- タレコミ氏の主張のように、本書はセキュリティを一切考慮していない
- 主な脆弱性は、XSS、SQLインジェクション、任意のサーバーサイド・スクリプト実行(アップロード経由)、メールヘッダインジェクション等
- 脆弱性以前の問題としてサンプルスクリプトの品質が低い。デバッグしないと動かないスクリプトが多数あった
- 上記に関連して、流用元のソースやデバッグ用のalertなどがコメントとして残っていて痛々しい
今時この水準はないわーと思いました。以前私がレビューした本は、最低でも、XSSやSQLインジェクションの二大脆弱性については一応の配慮があり、しかし漏れがあったり、他の脆弱性があったりという箇所を指摘していました。それに対して、本書は、セキュリティへの配慮は、見事なほどにありません。本書の索引には「セキュリティ」という項目はありますが、それはXAMPPのセキュリティ設定に関するもので、アプリケーションの脆弱性についての説明は一切ありません。
タレコミ氏からはレビューをお願いされたのですが、あまりに基本的な対策漏れを淡々と指摘しても、読者にとって有益な情報を提供できないと思いました。その一方で、本書ならではの視点が提供できることに気がつきました。それは、本書が基本的に静的HTMLとAjaxにより動的コンテンツを提供している点です。
AjaxアプリケーションのXSSは、拙著「体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践」では紹介していません。同書執筆時点で、AjaxのXSSに対してどう対策するべきなのか、十分整理できていなかったためです。そこで、議論のきっかけとして、本書のAjaxを用いたアプリケーションのXSSと対策案を紹介したいと思います。
本書のアプリケーションは下図(本書P20の図1-23から引用)に示す構成です。
まず、(1)でHTML5とJavaScriptによる画面がブラウザに表示されます。画面からは、XMLHttpRequest(jQeuryによる実装)によりPHP記述のAPIを呼び出し(2)、MySQLのクエリー(3, 4)の結果を返します(5)。受け取った結果は、DOMを用いて表示されます(jQueryによる実装)。基本的なAjaxアプリケーションの構成を押さえたものであり、意欲的な構成ですね。
今回は、まず基本的な画面表示のXSSを紹介します。
画面表示箇所(DOM)のXSS対策ができている例
狭義のXSSは、最後のDOMによる表示箇所のXSSということになりますが、意外なことに「セキュリティを全く意識していない」割にはこの部分のXSSは比較的少ないのです。それは、jQeuryのtextメソッドを用いているために、自動的にエスケープされているからです*1。その例をまず示しましょう。以下は、「Xmasパーティ予約情報参照」という画面の表示処理の部分です(本書P176リスト6-4より引用)。
function show(out){ var res = out.split("<i>"); $("#name").text(res[0]); $("#org").text(res[1]); $("#addr").text(res[2]); $("#tel").text(res[3]); $("#mail").text(res[4]); $("#course").text(res[5]); $("#nums").text(res[6]); $("#stat").text(res[7]); }
このスクリプトの実行例を下図に示します。textメソッドにより、小なり記号「<」などが適切にエスケープされ、XSS脆弱性はありません(下図)。
画面表示箇所(DOM)のXSS対策ができていない例
一方、画面表示箇所のXSS対策ができていない場合もあります。それは、table要素の組み立てなど、HTML断片を文字列連結で組み立てている箇所です。例を引用します(P209リスト7-4)。
var head = "<table border='1'><tr>"; head+="<th>ID</th>"; head+="<th>代表者名</th>"; head+="<th>所属組織</th>"; head+="<th>電話番号</th>"; head+="<th>コース</th>"; head+="<th>人数</th>"; head+="<th>登録日時</th>"; head+="<th>更新日時</th>"; $(function(){ $("#rev").click(function(){ $.get("revallxmas2.php", {}, show); }); }); function show(out){ var body = ""; var recs = out.split("<r>"); for(var i = 0; i <recs.length; i++){ body += "<tr>"; var flds = recs[i].split("<i>"); for(var j = 0; j <flds.length; j++){ if(j!=5){ body += "<td>" + flds[j] + "</td>"; // ← ココ }else{ body += "<td align='right'>" + flds[j] + "</td>"; // ← ココ } } body += "</tr>"; } body += "</table>"; $("#show").html(head+body); }
「ココ」とコメントしたところにXSS脆弱性があります。組み立てられたHTML断片は最終的にhtmlメソッドで描画されますが、flds[j]はHTMLエスケープされていないことがXSS脆弱性の原因となっています。この箇所では、「<td>」というタグとflds[j]という生データを文字列連結していますが、この時点でXSSがあることは確実ですね。
実際にXSSが起こっている画面例を以下に示します。alertの表示の他、打ち消し線の表示で、XSS脆弱性があることがわかります。
次にこのスクリプトにXSS対策してみましょう。表示の際にHTMLエスケープすればよいわけですが、サーバー(PHP)側でエスケープする方法と、JavaScript側でエスケープする方法があります。ここでは、表示の際にエスケープするという原則に従って、JavaScriptでエスケープすることにします。この問題は最後でもう一度議論します。
元のスクリプトはjQueryの機能を十分使っていないように思われるので、もう少しjQueryを活用する形で書いてみました。jQueryの使い方などでツッコミがあれば歓迎します。
function show(out){ var table = $("<table border='1'><tr><th>ID</th><th>代表者名</th><th>所属組織</th><th>電話番号</th><th>コース</th><th>人数</th><th>登録日時</th><th>更新日時</th>"); var recs = out.split("<r>"); for(var i = 0; i < recs.length; i++) { var flds = recs[i].split("<i>"); var row = $("<tr>"); for(var j = 0; j < flds.length; j++) { if(j != 5) { col = $("<td>").text(flds[j]); } else { col = $("<td align='right'>").text(flds[j]); } row.append(col); } table.append(row); } $("#show").empty().append(table); }
ご覧のように、table、行(変数row)、列(変数col)を用意して、appendメソッドで付け足していくという方法をとりました。テキストはtextメソッドで埋め込んでいるので、jQuery側でエスケープしてくれます。
同じデータで、対策版では以下のように表示されます。小なり記号「<」などがエスケープされている様子がわかります。
HTMLエスケープをどこで行うか
通常のWebアプリケーションの場合、HTMLで表示するパラメータのエスケープはサーバー側で行うしかありませんが、Ajaxアプリの場合、以下の選択肢があります。
- サーバー側で予めHTMLエスケープしておく
- サーバーからは生データを送り、ブラウザ上のJavaScriptでエスケープする
原則論から言えば、サーバーから生データを送るのが正しいように思います。Ajaxで受け取ったデータは表示するだけでなく、さまざまなデータ処理に活用する可能性があり、その場合は生データが便利です。また、表示の直前にエスケープするという原則にかなうからです。
この問題については、id:malaさんの以下のブログエントリも参考になります(Ajaxがテーマではありませんが共通する話題です)。
参考:HTMLのscriptタグ内に出力されるJavaScriptのエスケープ処理に起因するXSSがとても多い件について - 金利0無利息キャッシング – キャッシングできます - subtech
まとめ
Ajaxアプリケーションの単純なXSSについて説明しました。Ajaxを活用したアプリケーションであっても、HTMLとして出力するデータのエスケープは必要というあたりまえのことですね。
次回は、Ajaxとして受け取るデータの問題(XSSではなく、evalインジェクション等)について説明します。
[PR]
体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践
- 作者: 徳丸浩
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2011/03/01
- メディア: 単行本
- 購入: 119人 クリック: 4,283回
- この商品を含むブログ (146件) を見る