※ご注意
他社および他組織のWebサイトなどへのポートスキャンおよびデータの取得などの行為で得た情報を侵入などに悪用するか、または同じ目的を持つ第三者に提供した時点で違法となります。ご注意ください。
本稿の内容を検証する場合は、必ず影響を及ぼさない限られた環境下で行って下さい。
また、本稿を利用した行為による問題に関しましては、筆者および株式会社アットマーク・アイティは一切責任を負いかねます。ご了承ください。
「第1回 サーバのファイルが丸見え?!」では、「Forceful Browsing(強制的ブラウズ)」や「Path Traversal(パスの乗り越え)」によって、任意のファイルを読み出されてしまう危険性について紹介した。
今回は、Webアプリケーションを通じてSQL文を実行する攻撃や、OSコマンドを実行する攻撃について解説する。重要な企業情報や顧客データを守るためにぜひ活用してほしい。
SQL Injection(SQLインジェクション:SQLの挿入)
Webサイトを構成する要素の1つにデータベースがある。Webアプリケーションからデータベースを操作するために使われるのがSQLで、このSQLを偽造してデータベースの操作をしてしまおう、というのがこの攻撃だ。
アプリケーションの裏にデータベースがあり、データベースと連携してサービスを行っているシステムが攻撃対象となる。データベースに含まれるデータすべてを奪われる、削除されるなどの被害が想定される。
SQLはアプリケーション内のいろいろな個所で使われる。例えばユーザー名とパスワードを入力させてログイン処理をする場合を考えてみる。
次のようなSQLを使っているとしよう。
$SQL="SELECT * FROM user WHERE userid='$input_userid' AND password='$input_password'";
ユーザーにuseridとpasswordを入力させ、それらが一致したレコードが取り出されることを想定している。ところが、ユーザーからの入力が次のような場合はどうだろう?
input_userid | dummy |
---|---|
input_password | 'or'A'='A |
実際に実行されるSQLは、
SELECT * FROM user WHERE userid='≪dummy≫' AND password='≪'or'A'='A≫'
というSQLになり、すべてのレコードが該当するようなSQL文に変わってしまう。これにより、その中のいずれかのユーザーでログインできてしまうだろう。
また、次のようなこともできる場合がある。
input_userid | dummy |
---|---|
input_password | ';DELETE from user WHERE 'A'='A |
この場合に実行されるSQLは、
SELECT * FROM user WHERE userid='≪dummy≫' AND password='≪';DELETE from user WHERE 'A'='A≫'
となり、すべてのレコードが消えてしまう。このように、SQL文を挿入(Injection)することから、SQL Injectionと呼ばれる。
対策の1つは、ごく当たり前の「入力値チェック」と「サニタイジング」だ。入力値チェックで、各パラメータが想定しているパターンに合致するかを確認する、その後、SQL文を実行する際に、SQLで特殊文字として定義されている文字をサニタイジングすることになる。SQLの文脈とパラメータの挿入位置によりサニタイジングが必要な特殊文字は変わってくる。
以下にほとんどのRDBMSが準拠している「SQL92」での特殊文字を挙げる。
空白 ( ) | 文字列やコマンドの区切りに使われる |
---|---|
" ' , | 文字列の区切りに使われる |
- | 負の数を表すために使われる |
. | フィールドの指定に使われる |
& + / | | 演算子として使われる |
: | 変数の設定に使われる |
; | コマンドの区切りに使われる |
< = > | 演算子として使われる |
% ? * _ | 正規表現で使われる |
表1 SQL92での特殊文字 |
各RDBMSが個別に拡張している文字もある。各RDBMSのマニュアルを確認しておいていただきたい。どんな文字が特殊文字なのかを考えるのが面倒ならば、英数字だけしか入力できないようにしてしまうのも1つの手だ。
例えば、電話番号(03-1234-5678)を入力させたい場合に、1つの入力欄にすべての桁を以下のように入力させるのではなく、
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
<input type="text" size="13" value="03-1234-5678">
次のように3つの区分に分割しておくことで、数字だけに入力値を制限できるだろう。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
<input type="text" size="4" value="03">-<input type="text" size="4" value="1234">-<input type="text" size="4" value="5678">
もちろん、こういった工夫で回避できない場合も多々あるだろうが、可能な限り英数字だけに制限する方が安全だ。
コラム - 日本語はどうする?
英数字だけでなく日本語を入力させたい場合が多々あるだろう。
いまのところ、2バイト文字を使ってチェックを回避し、XSS、OS Command Injection(後述)、SQL Injectionを行う手法は見つかっていない。文字化けを回避するなどアプリケーションの動作のために日本語文字種の制限をすることはあるだろうが、いまのところセキュリティ的に気にする必要はないだろう。もちろん、今後何か手法が見つかるかもしれないので、そのときはこの限りではない。
もう1つの対策として、準備済みSQL文(プレースホルダ、バインドメカニズムと呼ばれることもある)を使う方法がある。自分でSQL文すべてを書くのではなく、SQL文の一部を可変に指定しておき、後でその部分を埋めて実行する、というような動作をする。
もともとは、繰り返し処理など似たようなSQL文を毎回コンパイル・実行する場合の性能低下を防ぐために考えられたものと思われるが、セキュリティ的にも有効だ。各言語で準備済みSQL文が使えるならば、多少プログラム行が増えてしまうことになるが積極的に使っていただきたい。
<% Set ObjConn = Server.CreateObject("ADODB.Connection") ObjConn.Open "sample","dbuser","dbpass" Set ObjCmd = Server.CreateObject("ADODB.Command") ObjCmd.CommandText= "SELECT * FROM user WHERE userid=? AND password=?" ObjCmd.CommandType = 1 ObjCmd.ActiveConnection = ObjConn userid = Request.QueryString("userid") Set ObjUserid = Server.CreateObject("ADODB.Parameter") ObjUserid.Value = userid ObjUserid.Size = Len(userid) ObjUserid.Type = 200 ObjCmd.Parameters.Append ObjUserid password = Request.QueryString("password") Set ObjPassword = Server.CreateObject("ADODB.Parameter") ObjPassword.Value = password ObjPassword.Size = Len(password) ObjPassword.Type = 200 ObjCmd.Parameters.Append ObjPassword Set ObjRS = ObjCmd.Execute() %>
これを使うことで、入力値チェックやサニタイジングをすべてシステムに任せることができる。
←「第1回」へ
Copyright © ITmedia, Inc. All Rights Reserved.