なぜPHPアプリにセキュリティホールが多いのか?
第42回 PostgreSQL 9.0に見るSQLインジェクション対策
PostgrSQL 9.
SQLインジェクション対策の4原則
基本的にはSQLインジェクション対策として以下の原則を守っていれば,
- すべてのパラメータを文字列としてエスケープする
- すべてのパラメータをプリペアードクエリのパラメータとして処理する
- 文字エンコーディングの設定をAPIで行う
- パラメータとして処理できない文字列はバリデーションを行う
原則1と原則2は重複して適用する必要はありません。どちらかを行います。文字エンコーディングの設定やプリペアードクエリのエミュレーション・
アプリケーションからDBMSを利用している場合,
パラメータとして処理できない文字列
「パラメータとして処理できない文字列」
典型的なSQLインジェクションに脆弱なコードは以下のようなコードです。
$sql = "SELECT * FROM ".$_GET['mytable']." WHERE tag = '".pg_escape_string($_GET['tag'])."' ORDER BY tag ".$_GET['order'];
このコードは次のようなSQL文を生成することを想定しています。
SELECT * FROM mytable WHERE tag = 'mytag' ORDER BY tag DESC
しかし,
例えば,
mytalbe; DELETE FROM mytable; --
$_GET['order']に
DESC; DELETE FROM mytable;
を挿入すれば,
プリペアードクエリの功罪
元々プリペアードクエリはセキュリティ対策としてではなく,
筆者はSQLインジェクション対策の基本はエスケープであると言い続けていました。しかし,
先ほどの例のようにテーブル名やSQL語句がパラメータとならないため,
- ※1
実際にはプリペアードクエリの場合,
プランナーがパラメータの値を使えないので高速化できない場合もあります。 - ※2
SQLite3のモジュール関数にエスケープ関数がない理由は,
SQLite3のライブラリで削除されていたことが原因です。PHPの開発者が省略したのではありません。
PostgreSQL 9. 0 libpqの新機能
libpqとはPostgreSQLのクライアントアクセス用のライブラリです。エスケープ関数を削除してしまうDBMSもあるなか,
- PQescapeLiteral
char *PQescapeLiteral(PGconn *conn, const char *str, size_
t length); PQescapeLiteralは,
SQLコマンド内で使用するために文字列をエスケープします。これは, SQLコマンド内のリテラル定数としてデータ値を挿入する時に有用です。特定の文字 (引用符やバックスラッシュ) は, SQLパーサによって特殊な解釈がなされないようにエスケープされなければなりません。PQescapeLiteralはこの操作を行います。 - PQescapeIdentifier
char *PQescapeIdentifier(PGconn *conn, const char *str, size_
t length); PQescapeIdentifierは,
テーブル, 列, 関数名などのSQL識別子として使用できるように文字列をエスケープします。これはユーザが提供した識別子に, そのままではSQLパーサで識別子として解釈されない特殊な文字が含まれる可能性がある場合, または, 大文字小文字の違いを維持しなければならない状況で識別子に大文字が含まれる可能性がある場合に有用です。
PQescapeLiteral
PQescapeLiteralは名前の通りパラメータを文字リテラル
$escaped = "'".pg_escape_string($val)."'";
としていた操作を,
$escpaed = pg_escape_literal($val);
とするだけで済むようになります。随分スッキリしますが,
この関数は見た目をスッキリさせるだけでなく
PQescapeIdentifier
PQescapeIdentifierはテーブル名やフィールド名などの識別子をエスケープする関数です。すべてのパラメータをプリペアードクエリのパラメータとして渡している場合であっても,
$escpaed_mytable_name = pg_escape_identifier($mytable_name);
となります。PHPのpgsqlモジュールにこの関数はまだありませんが,
先ほどのSQLインジェクションの例に出てきたORDER BY句のDESC/
- ※3
PQescapeIdentifierもPQescapeLiteralも内部的には同じPQescapeInternal関数を呼んでいます。
まとめ
SQLインジェクション対策の基本は
数値などを文字リテラルとして扱うとSQL文のパースに若干余分な時間が必要になります。文字リテラルはパーサによって別途パースされることがオーバーヘッドの理由です。しかし,
PostgreSQLプロジェクトでは正しいSQLインジェクション対策が行えるよう識別子をエスケープ処理できる専用のエスケープ関数が追加されました。まだMS SQL ServerやSQLite3のクライアントライブラリではSQLインジェクション対策の基本であるエスケープ関数がない状況です。しかし,
この記事に関連する書籍
-
Webアプリセキュリティ対策入門〜あなたのサイトは大丈夫?
本書は,Webサイトのセキュリティ確保のために必要な基礎知識と,安全なコードを書くために必要な基礎知識を解説しています。Webアプリケーションは比較的簡単に作成で...
-
はじめてのPHP言語プログラミング入門
Webアプリケーション構築ツールとしてPHPを取り上げた書籍は数多くありますが,言語の解説・入門書としての書籍はあまりありません。 本書は,プログラミング言語として...
バックナンバー
なぜPHPアプリにセキュリティホールが多いのか?
- 第46回 セキュリティ対策を考える上で欠かせないコンテクスト
- 第45回 入力バリデーションはセキュリティ対策
- 第44回 セキュリティ対策が確実に実施されない2つの理由
- 第43回 PHP 5.3のcrypt関数の問題
- 第42回 PostgreSQL 9.0に見るSQLインジェクション対策
- 第41回 PHP 5.3.4におけるセキュリティ上重要な仕様変更
- 第40回 MOPS:安全性の高いパスワードハッシュ作成ツール - phpass
- 第39回 MOPS:静的PHPソースコード脆弱性スキャナ RIPS
- 第38回 MOPS:PHPにおけるコード実行(2)
- 第37回 MOPS:PHPにおけるコード実行(1)