SQLインジェクション脆弱性を狙った大規模な攻撃が繰り返し行われ、
SQLインジェクション脆弱性が無くならない理由には以下のようなものが考えられます。
- 過去のコードやアプリケーションの再利用
- 基本的なセキュリティ知識不足
- セキュアコーディングプラクティスの未実施
- コード監査の不在
SQLインジェクション脆弱性の発見だけを目的にコード監査を行うことはあまりありませんが、
本題に入る前にSQLインジェクションの基礎知識クイズをしてみてください。いくつの質問に自信を持って答えられるでしょうか? すべて自信がある方はこの記事を読まなくても大丈夫な方です。
SQLインジェクションクイズ
SQLインジェクションに対する知識をクイズとしてお聞きます。○か×かで答え、
- SQLインジェクションはエスケープ処理を確実にしていれば大丈夫?
- プリペアードクエリを利用していれば大丈夫?
- SQLインジェクションはデータベース構造を知らないと攻撃が難しい?
- SQLインジェクションはWebアプリケーションファイアーウォールで防御できる?
- 文字エンコーディングベースのSQLインジェクションは文字エンコーディングが正しければ行えない?
SQLインジェクションを目的としたコードのチェック
PHPアプリの場合、
- 文字エンコーディングが正しいか検証し、
文字エンコーディング関連の処理を厳格に行う - クエリを生成する場合に
「すべて」 のパラメータを文字列として扱いエスケープ処理してクエリを生成する - プリペアードクエリ生成の際に、
プリペアード文にパラメータを入れない - テーブル名、
フィールド名をパラメータで指定する場合、 ホワイトリスト方式で確認する
コードチェックはこれらのベストプラクティスが実践されているかチェックすることが目的となります。これらのベストプラクティスを守っていなくてもSQLインジェクションが不可能なコードを記述可能ですが、
SQLインジェクション脆弱性チェックのポイント
チェックポイント:すべての入力がmb_
壊れた文字エンコーディングを利用したSQLインジェクションを防ぐためには文字エンコーディングが正しいか確認することが最も確実です。SQLインジェクション以外の攻撃にも壊れた文字エンコーディングが利用されるので、
$_POST、
チェックポイント:文字列のエスケープにデータベースAPI関数を利用し、
文字列のエスケープ処理を常に正しく行うには、
チェックポイント:すべてのパラメータをエスケープしているか?
文字列のエスケープは多少のオーバーヘッドが必要です。すべてのパラメータをエスケープするのは無駄に思えるかもしれませんが、
チェックポイント:すべてのパラメータを文字列として取り扱っているか?
前のチェックポイントですべてのパラメータがエスケープ処理されているか確認しましたが、
例えば、
$sql = 'SELECT * FROM user WHERE uid = '.pg_escape_string($_POST['uid']). ' AND gid = '. pg_escape_string($_POST['gid']). ';';
としても意味がありません。$_POST['uid'] に
1; DELETE FROM user; --
などが入っていればSQLインジェクションが可能です。すべての入力を文字列として取り扱えば、
$sql = "SELECT * FROM user WHERE uid = '".pg_escape_string($_POST['uid']). "' AND gid = '". pg_escape_string($_POST['gid']). "';";
ところで、
キャストした場合、
チェックポイント:データベースが利用する文字エンコーディングを変更する場合、
データベース文字エンコーディングをSJISに変更した場合に最も影響が大きいですが、
mysql_query("SET NAMES 'sjis';");
$result = mysql_query("SELECT * FROM user WHERE first_name = '".mysql_real_escape_string($_POST['first_name'])."' AND '".mysql_real_escape_string($_POST['last_name']) "';");
$_POST['first_ とエスケープ処理され、 チェックポイント:クエリを生成する場合に 実際時々見かける間違いですが、 このようなプリペアードクエリの作成ではまったくSQLインジェクション対策になりません。 チェックポイント:テーブル名、 テーブル名やフィールド名をパラメータで設定している場合、 PHPファイルすべてから LinuxやMac OSなどでは以下のようなコマンドで検索できます。 PHPファイルすべてから LinuxやMacOSなどでは以下のようなコマンドで検索できます。 PHPファイルすべてから PHPファイルすべてからfirst_
$prepared = "SELECT ". $_GET['field_name'] . " FROM " . $_GET['table_name']. " WHERE user = $1 AND group = $2";
実際の確認手順
1. ソースコード中にSQLインジェクションが可能となる可能性があるコードが無いか?
find . -name "*.php" | xargs grep -in "SET NAMES"
2. エスケープ関数が正しく使われているか?
find . -name "*.php" | xargs grep -in "mysql_real_escape_string"
3. クエリ実行箇所を確認し、
4. プリペアードクエリが利用されている箇所を確認し、