Webアプリケーションの脆弱性の解説&体験ページ

「マスタリングTCP/IP 情報セキュリティ編」(第2版) 8章で取り扱う攻撃を体験できる脆弱サイトの解説ページです。

このサイトでは、以下に示す「XSS攻撃」「SQLインジェクション」「CSRF攻撃」の3つについて、実際に攻撃を試すことができます。
本ページの右上には、「id」と「pass」とあるテキストボックスがあります。
「脆弱性サイトの仕組み」に示してあるIDとパスワードを入力をすることにより、当サイトのWebアプリケーションへログインできます。
なお、脆弱性体験用のWebアプリケーションは、敢えて脆弱性を作り込んでいますので、 それぞれの攻撃によって、どのようなことになるのかを確認できできます。
脆弱性体験用のWebアプリケーションの使い方については、「脆弱サイトの利用法」をご確認ください。

はじめに

脆弱性体験サイトの機能と仕組み

本サイトは以下の図のような構成を想定しています。

脆弱性体験サイトの仕組み

ユーザはログイン後、登録された検索したい会員名を、Webアプリケーションサーバで検索します。会員名は、以下に示すHTMLファイルのFormにある変数unameに格納して送信されます。

<form action="NotSecureSearchServlet" method="post">
                    検索したい会員の名前を入れて下さい(脆弱):<input type="text" name="uname" />
                    <input type="submit" value="検索" />
                  </form>

例えば、AIUEOという会員名で検索する場合に、Webブラウザから送信される値の一部を以下に示します。

uname=AIUEO

Webアプリケーションサーバは、Webブラウザから受け取った変数unameの値を用いてSQL文を構成します。構成されるSQL文を以下に示します。また、Webアプリケーションサーバは、構成したSQL文をDBMSに送信します。

SELECT * FROM user WHERE id='AIUEO'
このSQL文は、氏名(id)がAIUEOである会員の、すべての属性を射影します。

DBMSは、Webアプリケーションサーバから受け取ったSQL文を実行し、検索結果をWebアプリケーションサーバに送信します。

Webアプリケーションサーバは、DBMSから受け取った検索結果を表示するためのページを作成し、Webブラウザに送信します。Webブラウザは、受け取ったページを表示します。

データベースの内容

データベースには、以下の会員情報が登録されています。

会員情報

脆弱サイトの利用法

利用法その1(ログイン)

脆弱サイトにログインする場合は、次のユーザ名とパスワードを以下の箇所に入力して「ログイン」ボタンを押します。成功すると「ログイン成功」と書かれたダイアログが表示され、入力した箇所に「こんにちはuetoさん」と表示されます。

ユーザ名: ueto パスワード: aya

ユーザ名とパスワードの入力

利用法その2(会員検索)

ログイン後に「こんにちはuetoさん」をクリックすると、会員検索のためのテキストボックスが2つ表示されます。いずれかのテキストボックスに検索したい会員名を入力して、「検索」ボタンを押します(検索するテキストボックスは2つありますが、どちらでも構いません)。例えば、uetoで検索すると、以下の図のように表示されます。

uetoで検索した場合

利用法その3(会員検索の補足)

データベースに登録されていない会員名で検索した場合、データベースに登録されていない旨を伝える文が表示されます。 例えば、miuraで検索すると以下の図のように表示されます。 フォーム(脆弱)を利用した場合はユーザが入力した会員名が表示されますが、フォーム(preparedstatementの利用)を利用した場合はユーザが入力した会員名が表示されません。

フォーム(脆弱)を利用した場合

フォーム(preparedstatementの利用)を利用した場合

利用法その4(ログアウト)

「ログアウト」ボタンを押すと、脆弱サイトからログアウトします(2つのテキストボックスのどちらでも構いません)。

XSS攻撃

XSS攻撃の例

フォーム(脆弱)のテキストボックスに、下に示すXSS攻撃用のJavaScriptコードを会員名として入力し、検索を押して下さい。すると、次の図のようなダイアログが表示され、JavaScriptコードが実行されていることが確認できます。

    ueto<script>alert(document.cookie);</script>

XSS攻撃用のJavaScriptコード

結果

解説

上に示した、検索したい会員名としてのJavaScriptコードはデータベースに登録されていないので、Webアプリケーションサーバは登録されていない旨を伝えるページを作成しようとします。今回の例では、以下のページが作成されます。Webブラウザは、ueto以降のscriptタグ内(赤色の部分)をJavaScriptコードとして解釈し、alert(document.cookie);を実行します。

                <html>
        <body>
            検索結果:ueto<script>alert(document.cookie);</script> について該当するデータがございません。
        </body> 
    </html>

フォーム(脆弱)を利用した場合のページ

一方、フォーム(preparedstatementの利用)、正確には、Servlet(preparedstatementの利用)を利用する場合は、Webアプリケーションサーバは会員名をページに出力しないように設計されているので、以下のページが作成され、XSS攻撃は成功しません。preparedstatementについては、SQLインジェクションの章で説明します。

            <html><body>検索結果:該当するデータがございません。</body></html>

フォーム(preparedstatementの利用)を利用した場合のページ

SQLインジェクション

参考:マスタリングTCP/IP 情報セキュリティ編(第2版)
8.4節 SQLインジェクションとその対策

SQLインジェクションの仕組み

フォーム(脆弱)のテキストボックスに、下に示す攻撃のための文字列を会員名として入力し、検索を押して下さい。

 aiueo' or 'A'='A

すると、次の図のような表が表示されます。これは、データベースからすべての会員情報を不正に取得できてしまうことを示しています。

フォーム(脆弱)を利用した場合

一方、フォーム(preparedstatementの利用)を利用する場合はプリペアドステートメントを利用しているので攻撃は成功せず、以下の図のように「該当するデータがございません。」となります。

フォーム(preparedstatementの利用)を利用した場合

解説

フォーム(脆弱)を利用した場合

Webアプリケーションサーバ(Servlet)は、(ユーザが会員名として入力した)攻撃のための文字列をWebブラウザから受け取ると、下の(Javaの)文を実行します。

                    String sql = "SELECT * FROM user WHERE id = '" + id + "'";

" + id + "に攻撃のための文字列が代入された場合、変数sqlの値として、下のSQL文が構成されます。

                    SELECT * FROM user WHERE id='aiueo' or 'A'='A'

このSQL文の、WHERE句に注目すると、検索条件は「id(データベース中の氏名)が'aiueo'と同値、もしくは、'A'が'A'と同値かどうか」です。idがどんな値でも、'A'と'A'は常に同値なので、検索条件は常に真となります。そのため、データベース内の全ての行で検索条件が真となり、全ての行が選択されてしまいます。

フォーム(preparedstatementの利用)を利用した場合

Webアプリケーションサーバは、ユーザが会員名として入力した攻撃のための文字列をWebブラウザから受け取ると、下の文を実行します。

             
    // SQL文の用意. ?がプレースホルダです
    String sql = "SELECT * FROM user WHERE id = ?";
                
    // プリペアドステートメントオブジェクトの用意
    PreparedStatement ps = conn.prepareStatement(sql);
                
    // SQL文の1番目の?を、id(ユーザから受け取った攻撃のための文字列)に置き換え
     ps.setString(1, id);
    

この結果、データベースの氏名(id)が「攻撃のための文字列」である会員が存在しないので、攻撃が成立せず、単に、「該当するデータがございません。」と出ます。

※本サイトでは、セキュリティの観点から「SQLインジェクションの仕組み」で示した文字列の時のみリクエストを通すようにしています。

CSRF攻撃

参考:マスタリングTCP/IP 情報セキュリティ編(第2版)
8.5節 SQLインジェクションとその対策

前提

脆弱サイトは、cookieを利用してセッション管理をしています。そのため、ログアウトボタンを押してログアウトをしなければ、Webブラウザを閉じた後に、もう一度アクセスした場合でも、ログイン状態になるように設計されています。確認してみて下さい。

次に、ユーザがログアウトボタンを押した時の動作を説明します。ユーザがログアウトボタンを押すと、WebブラウザがWebアプリケーションサーバに対して下に示すリクエストを送信します。

  http://contents.saitolab.org/samples/LoginServlet?logout=true

このリクエストには、logout=trueというパラメータが記述されています。Webアプリケーションサーバは、このパラメータを受け取るとログアウト処理を行う設計になっています。

攻撃例

まず、脆弱サイトにログインして下さい。その状態で、悪友のmiuraちゃんから下に示すメールが届いたとします。あなたがメールに記載されているリンクをクリックすると、ログアウトしていないにもかかわらず、脆弱サイトから強制的にログアウトさせられてしまいます。確認してみて下さい。また、クリック後は更新(Ctrl+R)することで再度ログイン状態を維持することができます。

メール内容

miuraより あなたへ
一緒に幸せになりましょう。下のリンクをクリックして下さい。(はーと
幸せのページ

解説

メールに記載されているリンク(幸せのページ)のコードを抜粋し、下に示します。このリンクをクリックすると、前提で示したリクエストと同じリクエスト(赤色の部分)が、Webアプリケーションサーバに送信されます。送信するのは脆弱サイトにログインしているユーザ(あなた)なので、あなたのログアウト処理が行われてしまいます。より正確には、あなたがログインした状態の情報を持つcookieを、そのリクエスト時に送信することにより、logout=trueというパラメータが、Webアプリケーションサーバで解釈されます。

  <a href="http://www.saitolab.org/samples/LoginServlet?logout=true">幸せのページ</a>

当サイトのコンテンツや情報において、可能な限り正確な情報を掲載するよう努めておりますが、 誤情報が入り込んだり、情報が古くなったりすることもあり、必ずしもその内容の正確性および完全性を保証するものではございません。 当該情報に基づいて被ったいかなる損害について、一切責任を負うものではございませんのであらかじめご了承ください。