Web Application Security Memo

ウェブセキュリティに関するメモ書き

IPA ウェブ健康診断仕様を使ったWebアプリ脆弱性検査(SQLインジェクション編)

※当サイトにはプロモーションが含まれています。

公開日: 更新日:

ウェブ健康診断仕様

IPAが公開しているウェブ健康診断仕様の中にあるSQLインジェクションの診断をやってみます。

(ウェブ健康診断については、以前の記事 IPA ウェブ健康診断仕様とは?で説明しています)

診断内容

ウェブ健康診断仕様.pdf より抜粋

ウェブ健康診断仕様

診断する環境

  • クライアントOS:OS X
  • ブラウザ:Firefox (OWASP ZAPに対するプロキシ設定は済んでいるものとします)
  • HTTP通信を記録するツール:OWASP ZAP
  • 診断対象となるWebアプリケーション:
  • 診断対象となるWebページ:DVWAの「SQL Injection」ページ(Security Level: low)

※ OWASP BWA、DVWA(Damn Vulnerable Web Application) については、こちらの記事 OWASP BWA (The Broken Web Applications) とは? を参照して下さい。

手順1:環境を用意する

順番に起動していくだけなので詳細は省略します。

1-1. VirtualBoxを起動

1-2. VirtualBox内の OWASP BWA を起動

1-3. OWASP ZAP を起動

1-4. Firefox を起動

手順2:目的のWebページにアクセスする

2-1. OWASP BWAのトップページにアクセスする。

2-1-1. http://192.168.0.50/ にアクセスします。

OWASP BWAトップページ

2-2. DVWAのSQLインジェクションのページにアクセスする。

2-2-1. “Damn Vulnerable Web Application” をクリックして、DVWAにアクセスするとログインページが表示されます。

2-2-2. Usernameに “admin”, Passwordに “admin” を入力して[Login]ボタンをクリックし、DVWAにログインします。

DVWAログインページ

2-2-3. 画面左の[SQL Injection]メニューをクリックして、今回診断を行うページにアクセスします。

DVWAトップページ

DVWAのSQL Injectionページ

  • 入力フィールドを右クリックし、[要素を調査]を選択すると、開発ツールの画面が表示され、対象フィールドの name 属性が “id” であることが確認できます()。

手順3:診断を行う

今回対象となる入力フィールドの値は、データベース上のint型のカラムに対して比較されるため、3つの検出パターン全てを診断します。(「診断内容」項目に載せた画像内の、検出パターン3 - 備考列に、どの検出パターンを診断するかについての記述があります)

  • DVWAでは画面右下の [View Source]ボタンをクリックすることで、そのページで処理されるPHPのソースコードを表示することができます。
  • OWASP BWAでは http://192.168.0.50/phpmyadmin/ にアクセスすることで phpMyAdmin を使用することができます(DVWAはDBMSとしてMySQLを使用しています)。OWASP BWAのコンソールで使用するユーザ名・パスワードでログインできます。今回対象とするSQL Injection ページで使用されるのは、データベース “dvwa” の テーブル “users”です。

3-1. 検出パターン1

3-1-1. 検出パターン1の文字列を入力して[Submit]ボタンをクリックします。 検出パターン1の入力

3-1-2. OWASP ZAPでリクエストの中身を確認してみます。 検出パターン1のリクエスト

  • ブラウザのアドレスバーを見ると、この検索は http://192.168.0.50/dvwa/vulnerabilities/sqli/ というURLに対してリクエストされていることが分かります。従って、OWASP ZAPの画面左側にある[サイト]タブの中の [http://192.168.0.50\] -> [dvwa] -> [vulnerabilities] という階層の下にある[GET:sqli(Submit,id)]という行をクリックすると、画面右側の[リクエスト]タグや[レスポンス]タブに、該当する内容が表示されるようになります。
  • シングルクォート文字をURLエンコードすると、”%27” になります。これはOWASP ZAPに付属しているツールでも確認できます。(参照:OWASP ZAPの[エンコード/デコード/ハッシュ]ツール)

3-1-3. ブラウザ上ではエラーが表示されました。 検出パターン1の結果画面

  • 表示された文字列は「You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’’’’ at line 1」でした。
  • これはMySQLのエラーそのものです。

3-1-4. OWASP ZAPでもレスポンスの中身を確認します。 検出パターン1のレスポンス

3-1-5. 脆弱性の有無を判断します。

  • ウェブ健康診断仕様によると「レスポンスに DBMS 等が出力するエラーメッセージ(例:SQLExcepti on、Query failed 等)が表示された場合にエラーが発生したと判定します」とあるので、脆弱性有りと判断できます。

3-2. 検出パターン2

3-2-1. 検出パターン2の検索キーを 「a」「1」と決めます。(「1」を指定すれば条件に合致したデータが検索結果として表示されたため)

  • a」「1」と「a’ and ‘a’=’a」 「1’ and ‘a’=’a」という入力に対する、それぞれの検索結果を比較することになります。

3-2-2. 検索キー「a」 「1」を入力して[Submit]ボタンをクリックします。

2015-01-31_web_sindan_sql_injection_22_42

  • OWASP ZAPでリクエスト内容を確認する手順は省略します。

3-2-3. ブラウザ上では画面が更新され、入力した文字列が消えた状態になりました。 合致したデータが表示されました。

2015-01-31_web_sindan_sql_injection_24_13

3-2-4. OWASP ZAPでステータスコードを確認し、レスポンスの内容を保存します。

検出パターン2 検索キーの入力のレスポンス

  • ステータスコードは「200」でした。
  • レスポンスボディを保存します。
    • 以下のようにレスポンスの表示領域で右クリック [Save Raw] -> [レスポンス] -> [Body] を選択してファイルに保存します。
    • レスポンスを保存

3-2-5. 文字列 「a’ and ‘a’=’a」 「1’ and ‘a’=’a」を入力して[Submit]ボタンをクリックします。

2015-01-31_web_sindan_sql_injection_27_29

3-2-6. OWASP ZAPでリクエストの中身を確認します。

2015-01-31_web_sindan_sql_injection_28_14

3-2-7. ブラウザ上では画面が更新され、入力した文字列が消えた状態になりました。 先程と同じような検索結果となりました。

2015-01-31_web_sindan_sql_injection_27_45

追加のテスト

  • 「1’ and ‘a’=’b」という文字列で検索すると、合致するデータはありませんでした。つまり、「and」の後の等式が検索結果に影響を与えているようです。

2015-01-31_web_sindan_sql_injection_30_50

↓ submit

2015-01-31_web_sindan_sql_injection_30_51

3-2-8. OWASP ZAPでステータスコードを確認し、レスポンスの内容を保存します。

検出パターン2 "検索キー' and 'a'='a"入力のレスポンス

  • ステータスコードは「200」でした。
  • レスポンスボディの保存方法は、3-2-4と同様です。

3-2-9. 脆弱性の有無を判断します。

  • ウェブ健康診断仕様には、「HTTP ステータスコードが一致し、かつレスポンスのdiff(差分)が全体 の 6% 未満の場合、同一の結果と判定します。検査対象が検索機能の場合は、検索結果件数が同一の場合にも、同一の結果と判定します。」とあります。
  • 「検索キー」による検索結果と「検索キー’ and ‘a’=’a」による検索結果を比較します。
    • HTTP ステータスコードについて
      • 3-2-4と3-2-8の結果から、ステータスコードは同じでした。
    • レスポンスのdiff(差分)について
      • 3-2-4と3-2-8で保存したファイルをdiffコマンドで比較すると全く同じであることが分かりましたを比較すると変化量は約0.2%でした。
      • 以上から、「検索キー」による検索結果と「検索キー’ and ‘a’=’a」による検索結果は同一と言えます。
  • 以上から、脆弱性有りと判定できました。

3-3. 検出パターン3

3-3-1. 検出パターン3の検索キー(数値)を 「1」 と決めます。

  • 1」と「1 and 1=1」という入力に対する、それぞれの検索結果を比較することになります。

3-3-2. 検索キー(数値)「1」を入力して[Submit]ボタンをクリックします。

検出パターン3 検索キーの入力

  • OWASP ZAPでリクエスト内容を確認する手順は省略します。

3-3-3. ブラウザ上では画面が更新され、検索にヒットした1件のデータが表示された状態になりました。

検出パターン3 検索キーの入力結果

3-3-4. OWASP ZAPでステータスコードを確認し、レスポンスの内容を保存します。

検出パターン3 検索キーの入力のレスポンス

  • ステータスコードは「200」でした。
  • レスポンスボディの保存方法は、3-2-4と同様です。

3-3-5. 「検索キー and 1=1」を入力して[Submit]ボタンをクリックします。

検出パターン3 "検索キー and 1=1"の入力結果

3-3-6. OWASP ZAPでリクエストの中身を確認します。

検出パターン3 "検索キー and 1=1"入力のリクエスト

3-3-7. ブラウザ上では画面が更新され、検索にヒットした(らしき)1件のデータが表示された状態になりました。

検出パターン3 "検索キー and 1=1"入力の結果

3-3-8. OWASP ZAPでステータスコードを確認し、レスポンスの内容を保存します。

検出パターン3 "検索キー and 1=1"入力のレスポンス

  • ステータスコードは「200」でした。
  • レスポンスボディの保存方法は、3-2-4と同様です。

追加のテスト

  • 「1 and 1=2」という文字列で検索したところ、またもや同じ検索結果となりました。つまり、「and」の後の等式は検索結果に影響を与えていないようです。

3-3-9. 脆弱性の有無を判断します。

  • ウェブ健康診断仕様には、「同上」とあり脆弱性有無の判定基準は検出パターン2と同じになります。

  • 「検索キー」による検索結果と「検索キー and 1=1」による検索結果を比較します。

    • HTTP ステータスコードについて
      • 3-3-4と3-3-8の結果から、ステータスコードは同じでした。
    • レスポンスのdiff(差分)について
      • 3-3-4と3-3-8で保存したファイルを diffコマンド等で比較し、変化した部分のみを別のファイルに保存します。このファイルの文字数と3-3-4で保存したファイルの文字数を比較すると変化量は約2%でした。
      • 従って「検索キー」による検索結果と「検索キー and 1=1」による検索結果は同一とみなせます。
  • 以上から、脆弱性有りと判定できました。

  • 検出パターン3に含まれている「and」の後の等式が検索に影響を与えていないようなので、脆弱性の有無は判断できません。

手順4:脆弱性診断の結果

検出パターン1,2,3の全てにおいて脆弱性有りの判定となりました。

検出パターン1,2において脆弱性有りの判定となりました。

手順5:対策アドバイス

手順3に書いたように、[SQL Injection]ページの下にある[View Source]ボタンをクリックすると、このページの処理で使われているPHPのソースコードを確認することができます。これを見ると、GETパラメータをシングルクォートで囲んでそのままSQL文のWHERE句に埋め込んでいることが分かります。

SQL文の組み立てにプレースホルダを使用することが推奨されます。

その他

  • ウェブ健康診断仕様の「備考 (脆弱性有無の判定基準詳細、その他)」にある「レスポンス」とは、HTTPヘッダを含むのかどうか分かりませんでした。含んでも含まなくても、結果に大きな影響はないと思いますが、これは画面上の変化を比較したいのだろうと予想し、今回はHTTPヘッダを含まずボディの部分のみと判断しました。
  • 「検索キー」には、その値でサブミットして何らかの意味のあるデータが合致する値を選ぶのがよいです。
  • 「検索キー’ and ‘a’=’a」に対しては、「検索キー’ and ‘a’=’b」、「検索キー and 1=1」に対しては「検索キー and 1=2」のように「and」の後の等式が成立しない場合の検出パターンも試して、この等式の部分がサーバー側の処理に影響を与えているか確認するとよいです。
  • 今回のサーバー側の処理(PHP + MySQL)では、受け取ったパラメータ値をシングルクォートで囲み、そのままSQL文のWHERE句で使っていました。そのため検出パターン3では、MySQLの自動型変換により and以降が検索処理に影響を与えていませんでした。
  • 問題点等ありましたらご指摘頂けるとありがたいです。

[最終更新日: 2015年1月31日]