【セキュリティ】SQLインジェクション
SQLインジェクション
シングルクォートなどを用いてリテラルからはみ出した文字列をSQL文として認識させ、アプリケーションが呼び出すSQL文を変更する手法

※https://www.shadan-kun.com/waf_websecurity/sql_injection/ より画像引用

※https://activation-service.jp/iso/terms/2997 より画像引用

※https://www.kagoya.jp/howto/it-glossary/security/sql-injection/ より画像引用
一般的な動作

※https://web-scan.jp/article/1868/ より画像引用
対策がない場合の動作

※https://web-scan.jp/article/1868/ より画像引用
SQLインジェクション脆弱性がある場合に受ける可能性がある影響
・データベース内のすべての情報が外部から盗まれる
・データベースの内容が書き換えられる
・認証を回避される(IDとパスワードを用いずにログインされる)
・その他、データベースのファイルの読み出し、書き込み、プログラムの実行などを行われる
確実な対策
◎静的プレースホルダを利用してSQLを呼び出すこと
※プレースホルダによるSQL文の組み立て
プレースホルダ:SQLでは変数などでユーザーからの入力された値を受け取る、パラメータの部分に埋め込んでおく「?」などの記号のこと。
この「?」などのプレースホルダへ実際の値を割り当てることをバインドする、と表現することから、プレースホルダのことを別名「バインド変数」と呼ぶこともある。
このプレースホルダは、パラメータのバインド処理をデータベースエンジン側で実行するのか、アプリケーション側のライブラリ内で実行するのかによって、静的プレースホルダ(Prepared Statement)と動的プレースホルダの2つに分類される。
※静的プレースホルダ(Prepared Statement)

※https://envader.plus/article/45 より画像引用
SQLクエリ内で使用されるプレースホルダー(仮の置き換え要素)の一種で、特にプリペアドステートメント(Prepared Statement)やパラメータ化されたクエリで使われる。
静的プレースホルダは、バインド処理をデータベースエンジン側で行なう。
プレースホルダのついたSQL文はそのままデータベースエンジンに送信され、コンパイルなどの実行準備が行われ、この時点でSQL文が確定する。SQL文が確定した後に、受け取った値をプレースホルダへ割り当ててSQL文を実行するため、不正なSQL文へ変更されてしまう可能性がなくなる。
SQLインジェクションによる情報漏洩
① エラーメッセージを用いる方法
② UNION SELECTを用いる方法
UNION SELECT:2つのSQL文の結果の和集合を求める演算
UNION SELECTを用いた攻撃が成立すると、一度の攻撃で大量の情報が漏洩してしまう。

※https://www.scaler.com/topics/union-and-union-all-in-sql/ より画像引用
SQLインジェクションによる認証回避
ログイン画面にSQLインジェクション脆弱性があると、パスワードを知らなくてもログインできてしまう場合がある。

※https://xtech.nikkei.com/it/article/COLUMN/20060530/239449/ より画像引用
SQLインジェクション攻撃によるデータ改ざん

※https://xtech.nikkei.com/it/atcl/column/16/112500273/112500003/ より画像引用
その他の攻撃
データベースエンジンによって受ける可能性のある攻撃
・OSコマンドの実行
・ファイルの読み出し
・ファイルの書き出し
・HTTPリクエストにより他のサーバーを攻撃
SQLインジェクション攻撃により、データベースサーバー上のファイルの内容がデータベース経由で外部に漏洩する場合がある。
脆弱性が生まれる原因
リテラルの扱い
◎パラメータとして指定した文字列の一部がリテラルをはみ出すことにより、SQL文が変更されること。
リテラル:SQL文中で決まった値を示すもの
シングルクォートなどを用いてリテラルからはみ出した文字列をSQL文として認識させ、アプリケーションが呼び出すSQL文を変更することによりSQLインジェクション攻撃が成立する。
文字列リテラルの問題
SQLの標準規格では、文字列リテラルはシングルクォートで囲む。
「シングルクォートをエスケープする」:SQLでは文字列リテラル中にシングルクォートを含めたい場合は、シングルクォートを2つ続けて記述する決まりとなっている。
SQLインジェクションの攻撃イメージ

※『体系的に学ぶ安全なWebアプリケーションの作り方 第2版(固定版) 脆弱性が生まれる原理と対策の実践』より引用
数値項目に対するSQLインジェクション
Webアプリケーション開発によく用いられるスクリプト系言語(PHP、Perl、Rubyなど)は変数に型の制約がないため、数値を想定した変数に数値以外の文字が入る場合がある。
数値リテラルはシングルクォートで囲まないため、数値でない文字が現れた時点で数値リテラルは終了する。
対策
① プレースホルダによりSQLを組み立てる

※https://www.itmedia.co.jp/enterprise/articles/1201/30/news003.html より画像引用
プレースホルダ
SQL文の中で実際の値が入る部分を仮の記号(例: ? や :name)で置き換える仕組み。
変数や式など可変のパラメータの場所に埋め込んでおくもの。
プレースホルダを使うことで、SQL文の構造とデータを分離できる。
SELECT * FROM users WHERE id = ?;
? がプレースホルダで、後から実際の値(例: 1)がバインドされる。
※バインド:SQL文内のプレースホルダ(仮の記号)に実際のデータ・値を紐づける・割り当てること
プリペアドステートメント
SQL文を事前にコンパイル(準備)し、プレースホルダに値をバインドして実行する仕組み
プリペアドステートメントでは、SQL文の中にプレースホルダを使用して、後から値をバインドする。
// プリペアドステートメントの作成
$stmt = $pdo->prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
// プレースホルダに値をバインド
$stmt->bindValue(':name', 'John');
$stmt->bindValue(':age', 30);
// クエリを実行
$stmt->execute();
静的プレースホルダを指定する
PDO::ATTOR_EMULATE_PREPURES => false:静的プレースホルダ
PDO::ATTOR_EMULATE_PREPURES => true:動的プレースホルダ
※静的プレースホルダ

※『体系的に学ぶ安全なWebアプリケーションの作り方 第2版(固定版) 脆弱性が生まれる原理と対策の実践』より引用
値のバインドをデータベースエンジン側で行なう。
プレースホルダの付いたSQL文は、そのままデータベースエンジンに送られ、コンパイルなどの実行準備が行われ、SQL文が確定する。
次に、バインド値がデータベースエンジンに送られ、エンジン側で値を当てはめた後にSQL文が実行される。
プレースホルダの状態でSQL文がコンパイルされるため、後からSQL文が変更される可能性が原理的にありえない。
※動的プレースホルダ

※『体系的に学ぶ安全なWebアプリケーションの作り方 第2版(固定版) 脆弱性が生まれる原理と対策の実践』より引用
SQLを呼び出すアプリケーション側のライブラリ内で、パラメータをバインドしてからデータベースエンジンに送る方式。
② アプリケーション側でSQL文を組み立てる際に、リテラルを正しく構成するなど、SQL文が変更されないようにする
PDOの安全な利用法
PDOのコンストラクタの第4引数でオプションを指定する。
※PDO(PHP Data Objects)は、PHPでデータベースにアクセスするための軽量で一貫性のあるインターフェースを提供する拡張モジュール。PDOを使うことで、異なる種類のデータベース(MySQL、PostgreSQL、SQLiteなど)に対して、同じ方法でアクセスできるようになる。
PDOで例外処理を使用する
:PDO::ATTR_ERRMODEとしてPDO::ERRMODE_EXCEPTIONを設定
→ PDOの処理中にエラー発生した場合、例外をスローする設定。
→ これがないと、PDOデータはデータベース接続時のみ例外を発生する
複文の実行を禁止する
複文:複数のSQLの文をセミコロンで区切って一度に指定・実行すること
設定すると、SQLインジェクション攻撃の中で複文を使ったものを抑止することができる。
参考文献
『安全なWebアプリケーションの作り方』(徳丸本)
Discussion