SQLインジェクション

読み:えすきゅーえるいんじぇくしょん / 英語表記:SQL Injection

Webアプリケーションなどが外部から受け取った値を使ってデータベースへの命令文を組み立てる処理の不備を突き、値をSQL文の一部として解釈させて、データベースを不正に操作する攻撃である。値を命令文の構造から切り離して渡すプレースホルダを使うことが根本的な対策となる。

イマシルちゃんの解説

ネットショップや会員サイトは、商品や会員の情報をデータベースに保存しているんだ。
データを探すときは、SQLというデータベース用の言語で命令文を作って問い合わせるよ。
ただ、作りに問題があると、利用者が入力した文字がその命令文にそのまま組み込まれてしまう場合があるの。
細工された入力が命令の続きとして読まれると、見せるはずのない会員情報を取り出されることもあるんだ。
ログインの確認を素通りされたり、データを書き換えられたりする場合もあって、考えただけでおそろしいね。
このように、入力を使ってデータベースへの命令を勝手に変える攻撃をSQLインジェクションというんだよ。
入力をただの値として渡して、命令の形を変えさせないことが大切なんだ。

より深い解説

定義と位置付け

SQLインジェクションは、Webアプリケーションの脆弱性を利用する攻撃である。SQLはリレーショナルデータベースを操作するための言語で、データの検索・追加・更新・削除などを命令文として記述する。アプリケーションが利用者の入力を文字列として連結してSQL文を作ると、入力の内容によって命令文の意味そのものが変わることがある。

攻撃の対象は、アプリケーションの背後にあるデータベースである。XSSが利用者のブラウザでスクリプトを実行させるのに対し、SQLインジェクションはサーバ側のデータを直接ねらう。IPAの安全なウェブサイトの作り方では、取り上げる脆弱性の最初の項目として扱われている。

Webのフォームだけが入口ではない。URLのパラメータ、Cookie、HTTPヘッダー、APIのJSON、さらに一度データベースに保存された値など、SQL文の組み立てに使われる外部由来の値はすべて攻撃の入口になり得る。

命令文の意味が変わる仕組み

アプリケーションが利用者の入力をSQL文へ文字列として連結する実装では、入力に含まれる記号がSQLの構文として解釈されることがある。鍵になるのは、文字列の値を囲むシングルクォートである。

… WHERE id = '(入力されたID)' AND password = '(入力されたパスワード)'

入力値の中にシングルクォートが含まれると、アプリケーションが用意した文字列リテラルがそこで閉じたものとして扱われる。本来は「値」であるはずの続きの文字が、ORやANDといった条件式や別の命令の一部として読まれる。これにより、WHERE句の条件が常に真になるよう書き換えられたり、本来の命令に別の命令が付け足されたりする。

教科書的な例として、条件を常に成立させる ' OR '1'='1 のような文字列がよく挙げられる。認証処理で「該当する行が1件以上あればログインを認める」実装だと、この書換えによって、本来の資格情報を知らなくても認証を素通りしてしまう場合がある。

文字列リテラルの外にある数値の項目では、引用符を使わなくても条件式を注入できる。そのため、引用符だけに注目した対策では数値を扱う箇所を守れない。入力が「データ」として扱われるか「命令の構造」として扱われるかという一点が、この攻撃の本質である。

起こり得る被害

被害は、アプリケーションが使うデータベースアカウントの権限と、注入できる命令の範囲によって変わる。必ずすべてが起きるわけではないが、代表例は次のとおりである。

被害の例内容
情報の漏えい本来表示されない会員情報や他人のデータを取り出される
認証の回避条件を常に真にして、認証を素通りされる
データの改ざん・削除レコードの書換えや削除、ページの改ざんが行われる
権限昇格・侵入の踏み台権限の設定次第ではOSコマンドの実行などにつながることがある

画面に結果が表示されないサイトでも、条件が真のときと偽のときで応答や処理時間が変わることを手掛かりに、情報を1ビットずつ推測する手口がある。これをブラインドSQLインジェクションという。「結果が画面に出ないから安全」とは限らない。

似た攻撃との区別

名前が似た攻撃が多いため、「どこで何が意図せず解釈されるか」で区別する。

攻撃不正な解釈が起きる場所悪用するもの
SQLインジェクションデータベース値がSQLの構文として解釈されること
OSコマンドインジェクションサーバのOS値がOSコマンドの一部として解釈されること
クロスサイトスクリプティング利用者のブラウザ値がHTMLやスクリプトとして解釈されること
HTTPヘッダインジェクション応答を受け取るブラウザや中継装置値が改行を通じてヘッダーの構造に変わること

いずれも「外部からの値が、本来は構造を決める側の構文として解釈される」という共通の原因を持つ。対策の考え方も、値と構造を分離する点で共通している。

値と構造を分ける根本的な対策

最も確実な対策は、SQL文の構造をあらかじめ固定し、値の部分を別に渡すプレースホルダ(バインド機構)を使うことである。値が構文として解釈される余地がなくなるため、原理的に攻撃を防げる。IPAのSQLインジェクション対策でも最初に挙げられている。

プレースホルダには二つの方式がある。

方式値を結合する場所特徴
静的プレースホルダデータベースエンジン構文を先に解釈し、後から値を割り当てる。原理的に安全で推奨される
動的プレースホルダアプリケーション側のライブラリライブラリが値をエスケープして埋め込む。実装の品質に依存する

静的プレースホルダは、ISO/JISのSQL標準におけるプリペアドステートメントに相当する。データベースエンジンがSQL文の構造を先に確定させ、後から渡された値を常に「ただの値」として扱う。このため、値にどんな記号が含まれても構文は変わらない。

SELECT * FROM users WHERE id = ? AND password = ?

? の位置へ、利用者の入力を値として個別に渡す。文字列を連結してSQL文を組み立てる方式をやめることが要点である。

どうしても文字列連結が避けられない場合は、データベースエンジンが用意するエスケープ処理を使い、リテラルを正しく構成する。ただしこの方法はデータベースの種類ごとに違い、抜けも生じやすい。OWASPのSQLインジェクション対策は、エスケープだけに頼る方法を強く非推奨とし、あらゆる状況で防げるとは保証できないとしている。

テーブル名や列名、並び順(ASC/DESC)のように、プレースホルダで渡せない部分を外部入力で決める場合は、許可リストで既定の値に対応づけ、一致しない入力を拒否する。利用者の入力でテーブル名や列名を直接指定する設計自体を見直すことが望ましい。

被害を小さくする対策

次の対策は、実装の不備が残った場合に被害を抑えるためのもので、根本的な対策の代わりにはならない。

対策効果位置付け
入力値の検証仕様に合わない値を受け付けない自由入力の項目には効きにくい
エラーメッセージの抑制データベースの種類や失敗したSQL文を画面に出さない攻撃者への手掛かりを減らす
アカウント権限の最小化命令に必要な最小限の権限だけ与える侵入時の被害範囲を限定する
WAF既知の攻撃の特徴を持つ要求を遮断する一時的な緩和策

エラーメッセージをそのまま表示すると、データベースの種類や実行されたSQL文が攻撃者に伝わり、攻撃を組み立てやすくなる。利用者には一般的なエラー画面を返し、詳細はサーバ側のログにだけ記録する。

アプリケーションが使うデータベースアカウントには、その処理に必要な権限だけを与える。管理者権限で接続していると、侵入された場合にテーブルの削除や設定変更まで行われかねない。OWASPの資料も、ストアドプロシージャが自動的に安全なわけではなく、内部で動的にSQL文を組み立てれば同じ問題が起きると指摘している。

設計・運用での確認

午後での問われ方

この用語が出てくる記事

↑