脆弱性体験サイトをご覧いただきありがとうございます。
こちらの脆弱性体験サイトはリニューアルしました。
5秒後に新しいサイトへリダイレクトします。
ページが移動しない場合はこちらをクリックしてください。
はじめに、脆弱サイトの概要を説明します。このサイトは、データベースに登録された会員情報を検索するサービスを提供しています。
参考書マスタリングTCP/IP 情報セキュリティ編 8章 Webセキュリティ pp.205〜
<form action="NotSecureSearchServlet" method="post"> 検索したい会員の名前を入れて下さい(脆弱):<input type="text" name="uname" /> <input type="submit" value="検索" /> </form>例えば、AIUEOという会員名で検索する場合に、Webブラウザから送信される値の一部を以下に示します。
uname=AIUEO
SELECT * FROM user WHERE id='AIUEO'
このSQL文は、氏名(id)がAIUEOである会員の、すべての属性を射影します。
データベースには、右の表のように会員情報が登録されています。テーブル名は「user」です。
テーブル名:user
会員番号 | 氏名(id) | 電話番号 |
---|---|---|
1 | ueto | 1001 |
2 | saito | 1002 |
3 | goriki | 1003 |
4 | shina | 1004 |
5 | hamasaki | 9999 |
ユーザ名:ueto
パスワード:aya
フォーム(preparedstatementの利用)を利用した場合はユーザが入力した会員名が表示されませんが、フォーム(脆弱)を利用した場合はユーザが入力した会員名が表示されるようになっています。
フォーム(preparedstatementの利用)を利用した場合
フォーム(脆弱)を利用した場合
参考8.3 XSS攻撃とその対策 pp.220〜
XSS攻撃の確認は Firefox でお試し下さい。
ChromeやSafariにはXSS Auditor、IE 8以上には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の利用)を利用した場合の結果ページ
結果
参考8.4 SQLインジェクションとその対策 pp.226〜
フォーム(脆弱)のテキストボックスに、下に示す攻撃のための文字列を会員名として入力し、検索を押して下さい。
aiueo' or 'A'='A
攻撃のための文字列
すると、右の図のような表が表示されます。これは、データベースからすべての会員情報を不正に取得できてしまうことを示しています。
一方、フォーム(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'は常に同値なので、検索条件は常に真となります。そのため、データベース内の全ての行で検索条件が真となり、全ての行が選択されてしまいます。
Webアプリケーションサーバーは、ユーザが会員名として入力した攻撃のための文字列をWebブラウザから受け取ると、下の文を実行します。
// SQL文の用意. ?がプレースホルダです String sql = "SELECT * FROM user WHERE id = ?"; // プリペアドステートメントオブジェクトの用意 PreparedStatement ps = conn.prepareStatement(sql); // SQL文の1番目の?を、id(ユーザから受け取った攻撃のための文字列)に置き換え ps.setString(1, id);
この結果、データベースの氏名(id)が「攻撃のための文字列」である会員が存在しないので、攻撃が成立せず、単に、「該当するデータがございません。」と出ます。
参考8.5 CSRFの攻撃とその対策 pp.230〜
脆弱サイトは、cookieを利用してセッション管理をしています。そのため、ログアウトボタンを押してログアウトをしなければ、Webブラウザを閉じた後に、もう一度アクセスした場合でも、ログイン状態になるように設計されています。確認してみて下さい。
次に、ユーザがログアウトボタンを押した時の動作を説明します。ユーザがログアウトボタンを押すと、WebブラウザがWebアプリケーションサーバーに対して下に示すリクエストを送信します。
http://contents.saitolab.org/samples/LoginServlet?logout=true
このリクエストには、logout=trueというパラメータが記述されています。Webアプリケーションサーバーは、このパラメータを受け取るとログアウト処理を行う設計になっています。
まず、脆弱サイトにログインして下さい。その状態で、悪友のmiuraちゃんから右に示すメールが届いたとします。あなたがメールに記載されているリンクをクリックすると、ログアウトしていないにもかかわらず、脆弱サイトから強制的にログアウトさせられてしまいます。確認してみて下さい。
メールに記載されているリンク(幸せのページ)のコードを抜粋し、下に示します。このリンクをクリックすると、前提で示したリクエストと同じリクエスト(赤色の部分)が、Webアプリケーションサーバーに送信されます。送信するのは脆弱サイトにログインしているユーザ(あなた)なので、あなたのログアウト処理が行われてしまいます。より正確には、あなたがログインした状態の情報を持つcookieを、そのリクエスト時に送信することにより、logout=trueというパラメータが、Webアプリケーションサーバーで解釈されます。
<a href="http://www.saitolab.org/samples/LoginServlet?logout=true">幸せのページ</a>
メール内容