安全なSQLの呼び出し方 SQLインジェクション対策【個人的なお勉強アウトプット】


参考記事

chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/viewer.html?pdfurl=https%3A%2F%2Fwww.ipa.go.jp%2Ffiles%2F000017320.pdf&clen=730235&chunk=true

SQLインジェクション攻撃とは

SQL文のリテラル部分は変動するパラメータ化することが一般的。
リテラルとして文法的に正しく文を生成しないと、パラメーターに与えられた値がリテラルの外にはみ出した状態になり、リテラルの後ろに続く文として解釈されることになる。

$q = "SELECT * FROM atable WHERE id='$id'";

$idに以下の値を与える場合、
';DELETE FROM atable--

パラメータを展開した後の SQLは以下になる。
SELECT * FROM atable WHERE id='';DELETE FROM atable--'

結果、データベースの内容がすべて削除される。

安全なSQLの呼び出し方

  • 文字列連結によるSQL文の組み立て
  • プレースホルダによるSQL文の組み立て
    • 静的プレースホルダ
    • 動的プレースホルダ

文字列連結によるSQL文の組み立て

パラメータ部分への値の埋め込みを文字列連結によって実現する。
ただし、以下のプログラムにはSQLインジェクションの脆弱性がある。

$name = $_POST['name']; //... $sql = "SELECT * FROM employee WHERE name='" . $name . "'"; 

PHPの式$nameが「山田」の場合、以下のSQL文が生成される。
SELECT * FROM employee WHERE name='山田'
SQLインジェクションの脆弱性をなくすには$nameをクォートとして文字列連携する際に、$nameの値に対するエスケープ処理が必要。エスケープが必要な文字はデータベースエンジンの種類や設定によって異なる。

文字列連結による安全なSQL文生成

以下の要件を満たす必要がある。

  • 文字列リテラルに対しては、エスケープすべき文字をエスケープすること
  • 数値リテラルに対しては数値以外の文字を混入させないこと

しかし、文字列リテラル生成時にエスケープが必要な文字は、データベースエンジンの種類によって異なったり、データベースの背rっていによって異なる場合がある。データベースの種類や設定に応じたエスケープ処理を行わないといけない。
なので、SQLにおけるリテラルを文字列として生成する専用のメソッドや関数を用意している場合がある
例えば、PHPのPear::MDB2、ODOなど。

PHPのPEar::MDB2におけるquateの呼び出し
//ライブラリのロード
require_once 'MDB2.php'; 

//DBへの接続(PostgreSQLの場合)
$db = MDB2::connect('pgsql://dbuser:password:hostname/dbname?charset-utf=8';)

//文字列型を指定して、文字列チレたるのクォート済み文字列を得る
$db->quote($s, 'text')

//数値を指定して、数値リテラルの文字列を得る
$db->quote($n, 'decimal')

上記のようにquoteメソッドの第2引数で生成するリテラルの型を指定する。
textで文字列型を指定すると、データベースエンジンの種類や設定に応じて適切なエスケープ処理を施し、それをシングルクォートでくくった文字列を返す。
decimalなどの数値型を指定すると、数値リテラルとしてふさわしい文字列を返す。

プレースホルダによるSQL文の組み立て 静的プレースホルダ

プレースホルダのままのSQL文をデータベースエンジン側にあらかじめ送信して、実行前にSQL分の構文解析などの準備をしておく。SQL実行の段階で、実際のパラメータの値をデータベースエンジン側に送信し、データベースエンジン側がバインド処理をする。
SQL文の構文がバインド前に確定することから、プレースホルダにわたす文字列はクォートして記述する必要がない。そのため、シングルクォートのエスケープ処理も必要ない。数値リテラルも適切にバインドされる。
このことから、セキュリティの観点で、静的プレースホルダはもっとも安全。静的プレースホルダでは、SQLを準備する段階でSQL文の構文が確定し、後からSQL構文が変化することがないため、パラメータの値がリテラルの外にはみ出す現象がおきない。結果、SQLインジェクションの脆弱性が生じない。

プレースホルダによるSQL文の組み立て 動的プレースホルダ

プレスホルダを利用するものの、パラメータのバインド処理をデータベースエンジン側で行うのではなく、アプリケーション側のライブラリで実行する方式。セキュリティの観点では、プレースホルダを用いたバインド処理によってパラメータの値の埋め込みがライブラリで機械的に処理されることから、文字列連結による組み立てに比べてアプリケーション開発者のミスによるエスケープ漏れを防止を期待できる。
ただし、動的プレースホルダは静的プレースホルダとは異なり、バインド処理を実現するライブラリによってはSQL構文を変化させるようなSQLインジェクションを許してしまう脆弱な実装のものが存在する可能性を否定できない

動的プレースホルダでも安全な書き方があるみたいだが割愛。