Structured Query Language(SQL)は、データベース内のデータを操作、管理するために設計された言語です。SQLは誕生以来、多くの商業的およびオープンソースのデータベースとして着実に普及してきました。SQLインジェクション(SQLi)は、特別に作成されたSQLステートメントを使用してデータベースを狙い、システムに対して想定外で不適切な動作を行わせます。
ビデオ(英語)を観てSQLインジェクション攻撃について学ぶことも可能です。
攻撃が成功した場合、以下のような被害が考えられます。
SQLインジェクションが完全に成功してしまうと、あらゆる企業や個人に甚大な被害を及ぼす可能性があります。攻撃によっていったんデータが危険にさらされると、完全に回復することは困難です。
データベースは一般的にアプリケーション(例えばユーザーに入力を要求し、それに基づきデータベース内を検索するウェブサイトなど)を通じて狙われますが、直接標的になる場合もあります。SQLインジェクション攻撃は、OWASPの企業が対策を講じているアプリケーションセキュリティリスクのトップ10に挙げられています。
SQLインジェクション攻撃には多くの方法があります。攻撃者は特定の攻撃ベクトル/方法を選択する前に、まずシステムの行動を観察します。
サニタイズされていない入力はSQLi攻撃の一般的なタイプで、攻撃者はエスケープ処理するべき文字として十分にサニタイズされていない、または正確/想定通りの種類であると検証されていない入力を提供します。
例えば、オンラインで請求書の支払いをするウェブサイトで、ウェブフォームにユーザーのアカウント番号を要求し、それをデータベースに送って関連するアカウント情報を抜くというものです。ウェブアプリケーションがユーザー提供のアカウント番号で動的にSQLクエリ文字列を作成している場合、以下のように見えるでしょう。
“SELECT * FROM customers WHERE account = ‘“ + userProvidedAccountNumber +”’;”
これはアカウント番号を正しく入力しているユーザーにとって機能する一方で、攻撃者が侵入する隙を残します。例えば、ある人が “‘ または ‘1’ = ‘1”というアカウント番号を提供した場合、以下のようなクエリ文字列になります。
“SELECT * FROM customers WHERE account = ‘’ or ‘1’ = ‘1’;”
‘1’ = ‘1’が常にTRUEと評価されるため、このステートメントをデータベースに送ると、1人の顧客ではなく全顧客のデータが返されます。
インファレンシャルSQLインジェクションとも呼ばれるブラインドSQLインジェクション攻撃は、標的であるデータベースからの直接データを公開しません。むしろ、攻撃者は行動の間接的な手がかりを細かく調べます。攻撃者の目的によって、HTTPレスポンス内の詳細、特定のユーザー入力のための空白ウェブページ、そして特定のユーザー入力に対するデータベースの応答時間の長さなどが手がかりとなります。これらは攻撃者が試すことのできる別のSQLi攻撃場所を指し示すこともあります。
この種の攻撃は若干複雑で、単一の直接的クエリ応答攻撃で目標を達成できなかった場合に攻撃者が利用する場合があります。通常、攻撃者はデータベースに提示された場合、データベースシステムが攻撃者のコントロール下にある外部サーバーとの接続を確立するようにトリガーするSQLステートメントを作成します。このような方法では、攻撃者はデータを収穫したり、データベースのの行動をコントロールしたりする可能性があります。
セカンドオーダーインジェクションは、Out-of-Bandインジェクション攻撃の一種です。この場合、攻撃者はデータベースシステムの別の行動によって保存され、実行されるSQLインジェクションを提供します。セカンダリーシステム行動(時間ベースのジョブ、またはその他の典型的な管理者やユーザーのデータベース使用など)が発生し、攻撃者のSQLインジェクションが実行されると、攻撃者がコントロールするシステムへの「リーチアウト」が起こります。
ここで紹介するSQLインジェクションの例では、ユーザーと連絡先という2つのデータベーステーブルを使用します。ユーザーテーブルには、単純にID、ユーザー名、パスワードという3つのフィールドがあります。連絡先テーブルにはユーザーID、苗字、名前、住所1、メールアドレス、クレジットカード番号、セキュリティコードなど、より多くの情報があります。
ユーザーテーブルの情報は以下のようにログインに使用されます。
注:データベースに保存するパスワードはクリアテキストにせず、常にハッシュ化してソルトを加える必要があります。
ログインする際、ユーザーはログインページからユーザー名とパスワードを入力します。この情報はウェブサーバーに送られ、そこでSQLクエリが作成され、そのクエリがデータベースサーバーに送られます。このようなクエリは以下のように表示されます。
Select ID from Users where username=’jsmith’ and password=’P@$$w0rd’
SQLの仕組みとして、その後クエリが要求する各行で比較演算子を実行します。この例では、クエリはユーザーテーブルをチェックし、ユーザー名がjsmithでパスワードがP@$$w0rdになっている全ての行のID値を返すよう要求しています。多くの場合、ウェブサーバーはデータベースサーバーが返すもの、そしてそれが数字であるかを確認します。ここでは、ウェブサーバーは「1」を受け取りユーザーにログインページを通過させます。
しかし、ここで悪意を加えるとしたらどうでしょう。データベースサーバーがtrue-or-falseチェックを行うため、認証が成功したと思い込ませることができます。パスワードにORを追加すれば良いのです。「x'」または「1=1」をパスワードにしてログインした場合に作成される新しいクエリは以下のようになります。
Select ID from Users where username=’jsmith’ and password=’x’ or 1=1
xはjsmithのパスワードではありませんが、データベースは2つ目の条件を確認するため、これで動作します。xがjsmithのパスワードでない場合、1=1となります。IDはアプリケーションに送られ、ユーザーの認証が完了します。
これは1=1の条件でなくても結構です。平等な値であれば、2=2、4726=4726またはa=aでも動作します。
ウェブページがデータを表示できるのであれば、追加データをスクリーンに印刷することも可能です。データにアクセスするために、2つのSQLリクエストをまとめることもできます。‘ または1=1に加えて、連絡先からのUNION SELECT 苗字、クレジットカード番号、セキュリティコードといった2つ目のステートメントを追加することもできます。このような追加条項には余分な作業が必要となりますが、SQLインジェクション攻撃の最終目標はデータへのアクセスを得ることです。
ブラインドSQLインジェクションで使えるもう1つのテクニックは、データがスクリーンに戻されない場合に他のヒントを注入するというものです。‘ または1=1の条件と同様、サーバーにスリープするよう指示することができます。“ ‘ or sleep(10) ”と付け加えることで、それらしい動作が行われます。データベースを10秒間眠らせて、すべての応答を遅らせます。
SQLインジェクション攻撃の成功を防ぐには、以下のような方法があります。
動的SQLを使用しない
ユーザー提供入力のサニタイズ
プレインテキストに機密データを残さない
データベース権限と特権の制限
データベースエラーを直接ユーザーに表示しない
データベースにアクセスするウェブアプリケーションに、ウェブアプリケーションファイアウォール(WAF)を使用する
ウェブアプリケーションのテスティングソリューションを使用して、データベースとインタラクトするウェブアプリを定期的にテストする。
データベースを最新のパッチに更新する
SQLインジェクション攻撃者に最も人気のある攻撃法ですが、データの暗号化、ウェブアプリケーションの保護およびテスト、そして最新のパッチの使用などの十分な対策を講じることで、データを安全に保持するための有意義な一歩を踏み出すことができます。