こんにちは。
前回こちらの記事で、SQLインジェクションについてのクイズを書きました。
前回記事:SQLインジェクションでデータベースを攻撃(!)してみよう
今回は、これらのクイズの解答例を掲載させていただきます!
まずクイズのおさらいです。
【問題】
下記のような構造のテーブルがあります。
employeeテーブル
addressテーブル
employeeテーブルのidと、addressテーブルのemployee_idにはリレーションが張ってあります。(と思ってください。)
また、実行されるプログラムは、下記のものになります。
1 2 3 |
$name = $_POST['name']; $sql = 'SELECT id, name FROM employee WHERE name = \'%'.$name.'%\' AND created >= \'2015-05-01 00:00:00\''; //→このあとDB接続処理、SQLの実行処理が続きます(割愛) |
このとき、下記それぞれの動作を実現させるためには、$_POST[‘name’]としてどのような値をPOSTすればよいでしょうか。
Q1.全社員のid、名前を取得する
Q2.employeeテーブル内のデータを全削除する
Q3.全社員の住所情報を取得する
Q1の解答例
単純にnameの値を指定すると、そのユーザーのデータしか取得することができないので、
全データを表示させるようにWHERE句の記述を変えてあげる必要があります。
たとえば、
1 |
$name = '\' OR 1 = 1; -- '; |
を指定することで、
1 |
SELECT id, name FROM employee WHERE name = '%' OR 1 = 1; -- %' AND created >= '2015-05-01 00:00:00' |
というSQL文が流されることになり、
のように他の社員のデータを取得することができます。
まず、最初に「\’」を指定することでクォートで囲われた部分を終わらせ、また最後に「;–」を指定することで、後に続くSQLの条件をコメントアウトします。
また、全データを条件として指定する際に、「OR 1=1」と指定することで、WHERE句の条件を無効化しています。
Q2の解答例
最初と最後にそれぞれ「\’」と「;–」を指定する必要があるのはQ1と同じですが、その中でテーブルのデータを削除するSQL文を書いてあげる必要があります。
たとえば、Q1の値の続きにSQL文を追記し、
1 |
$name = '\' OR 1 = 1; DELETE FROM employee; -- '; |
といった値を指定することで、SQL文は、
1 |
SELECT id, name FROM employee WHERE name = '%' OR 1 = 1; DELETE FROM employee; -- %' AND created >= '2015-05-01 00:00:00' |
が実行され、
のように社員のデータを全削除することができます。
今回はデータの削除でしたが、同様にDELETE文のところに別のSQL文を指定することにより、データの書き換えや、果てはDB設定の変更など、様々な動作を起こすことができます。
Q3の解答例
Q2のロジックと同じで、DELETE文のところにJOINさせたSELECT文を指定することで実現できそうですが、ちょっとだけ別のやり方をご紹介します。
たとえば、
1 |
$name = '\' OR 1 = 1 UNION SELECT employee_id, address FROM address; -- '; |
とすると、実行されるSQLは
1 |
SELECT id, name FROM employee WHERE name = '%' OR 1 = 1 UNION SELECT employee_id, address FROM address; -- %' AND created >= '2015-05-01 00:00:00' |
となり、
という結果が出力され、社員の住所を知ることができます。
このように、自分が実行したいSQLをうまく動作させるにはいくつかパターンがあり、
・「;」で一度SQL文を終わらせてから、実行したいSQLを記述する
・UNIONでSQL文をつなげることで、実行したいSQLを記述する
などのパターンがあります。
SQLインジェクションはパターンがある
今回、3問ほどクイズを出題させていただきました。3問ともやっていただいた方はお気づきかと思いますが、SQLインジェクションには決められたパターンがあります。
たとえば、自分が実行したいSQLをちゃんと動作させるために、前後に書かれたSQL文を無効化、もしくは無難に終わらせる必要があります。
そのため、多くの場合はSQLインジェクションを行うための入力値として、最初に「\’」を持って来たり、また後には「;–」と書いて以降のSQLを無効化します。
要は、これらの値が入ってきてしまう可能性があるプログラムの書き方をしていれば、SQLインジェクションが発生してしまうということになります。
こういった理由から、プリペアドステートメントを使うなど、適切なSQLインジェクション対策を行わなければいけないのです。
プリペアドステートメントの使い方については、なんだかんだPHPのマニュアルが一番なので、こちらを参考にしていただけたらと思っております。
http://php.net/manual/ja/pdo.prepared-statements.php
まとめ
・SQLインジェクションを行うには、いくつかのパターンに則らなければならない。(「\’」や「;–」を用いる等)
・なので対策を行う際には、そのパターンが再現できなくなるような組み方にすればよい(キャストによる型変換、プリペアドステートメントなど)
今回の記事のまとめは、この2つに尽きると思います。
普段からプリペアドステートメントを使うように意識するだけで対策にはなりますが、それだと対策方法は知っていても「SQLインジェクションそのもの」について理解していることにはなりません。
プリペアドステートメントは「なぜ」使わなければならないのか、脆弱性の本質についてきちんと理解することで知識が深まり、同時に脆弱性に対する危機感も自覚することができるので、プログラムを書く際にも「気を付けなければいけない」という意識が生まれてくると思います。
ちなみに、今回僕が紹介した解答例は模範解答などでは全然なく、もっとスマートなやり方などいろいろあると思いますので、他の攻撃手法がないか、いろいろ試してみてくださいね!
それでは!