SQL インジェクション
不正な SQL を投入することで、通常はアクセスできないデータにアクセスしたり更新したりする攻撃
一般的な影響
- データベースの非公開情報を閲覧される
- データベースの内容を改ざん、消去される
- 認証回避により不正にログインされる
- ストアドプロシージャなどを利用した OS コマンドを実行される
例
- 「name='SHOEISHA' AND passwd = ''」と常に真となる「'1' = '1'」 との OR 条件となり、その結果、members のすべてのレコードが選択されることになる
種類
文字リテラルに対する SQL インジェクション
- SQL 文の文字列要素に SQL 文の断片や SQL コメントを入力することでデータベースへ不正な操作が行われるタイプ
SELECT * FROM user WHERE name = 'yamada';
数値リテラルに対する SQL インジェクション
- SQL 文の数値要素に、 SQL 文の断片や悪意のある SQL 文を入力することで、データベースへ不正な操作が行われるタイプ
SELECT * FROM user WHERE age > 25;
SQL インジェクションの脆弱性があるサイトの動作
- 画面入力パラメータに「'」を入れて送信するとデータベースエラーが発生する
脆弱性例
脆弱性あるコード
<?php $db = $this -> get_db(); $id = $_GET["id"]; $name = $_GET["name"]; // 脆弱なコード $stmt = $db->prepare("SELECT * FROM user WHERE id = '" . $id . "' AND name = '" . $name . "' ;"); $stmt->execute(); ?>
修正コード
<?php $db = $this -> get_db(); $id = $_GET["id"]; $name = $_GET["name"]; $stmt = $db->prepare("SELECT * FROM user WHERE id = ? AND name = ?;"); $stmt->execute(array($id,$name)); ?>
対策
- プレースホルダによる SQL 文の組み立てを行なう
- 検索条件などのパラメータ部分を「?」 などの記号で示しておき、後から実際の値を機械的な処理で割り当てる方法
- 実際の変数を割り当てることを「バインドする」と呼ぶ
- 種類
- 静的プレースホルダ(プリペアドステートメント)
- プレースホルダのままの SQL 文をデータベース側にあらかじめ送信して、実行前に、SQL 文の構文解析などの準備をしておく方式
- SQL 実行の段階で、実際のパラメータの値をデータベース側に送信し、データベース側がバインド処理
静的プレースホルダ
は、SQLを準備する段階でSQL文の構文が確定し、後から構文が変化することがないため安全
- 動的プレースホルダ
- 静的プレースホルダとは異なり、プレースホルダを利用するものの、値のバインド処理をデータベース側で行うのではなく、ウェブアプリケーション側のライブラリ内で実行する方式
動的プレースホルダ
はライブラリの実装に問題があると、SQL 文を変化させるような SQL インジェクションを許してしまう可能性があるため注意が必要- 静的プレースホルダがサポートされているデータベースでは、静的プレースホルダを使用しましょう
- 静的プレースホルダ(プリペアドステートメント)
- 文字列連結により SQL 文を組み立てる場合は、文字列をエスケープする必要があります。エスケープするべき特殊文字はデータベースによって異なるため、エスケープを行う関数やライブラリ、データベースのエスケープ関数などを使用するようにしましょう。
- WAF の利用
- RDBMS のアクセス権設定を必要最低限にする
- プレースホルダによる SQL 文の組み立てを行なう
原因
- 外部からの入力に対する値のチェックが不十分
- 特殊文字のエスケープ
特殊文字
- 文字列やコマンドの区切り
- スペース
- "
- '
- ,
- ;
- 変数の設定
- :
- 演算子
- <
- =
- &
- &
- /
- |
- フィールドの指定
- .
- 正規表現で使われる
- %
- ?
- _
- 文字列やコマンドの区切り
Blind SQL インジェクション
SQL 文は検索結果の表示以外にも使われます。例えば、利用者登録画面があるとして利用者が任意のIDを使用することができるとします。しかし、一意に管理するためにはIDの重複を避けなければいけません。この際に、一般的にIDの重複チェック機能を実装
このときにSQL文の検索結果を使いますが、表示や更新は行っていません
下記では入力したIDが使われていたら「既に使われているIDです」そうでなければ、「使用可能です」と返す処理を行います。
$stmt = $db->prepare("SELECT * FROM user WHERE id= '" . $_GET["id"] . "' "); $stmt->execute(); $user = $stmt->fetch(); if ($user) { $message="既に使われているIDです。"; } else { $message="使用可能です。"; }
- このように実行結果を画面へ出力しない SQL 文において、SQL インジェクションの脆弱性があることを、
ブラインド SQL インジェクション
の脆弱性と呼ぶ
- このように実行結果を画面へ出力しない SQL 文において、SQL インジェクションの脆弱性があることを、