【セキュリティ最前線】
セキュリティホールをついて遊ぶ
第2回:PHPのSQLインジェクションを実体験
著者:大垣 靖男
公開日:2008/1/18(金)
文字エンコーディングベースのSQLインジェクションの防止方法
現在のPostgreSQLやそのほかのデータベースサーバでは、不正な文字エンコーディングを受け入れない。しかし、PostgreSQLやMySQLのように「\」をエスケープ文字として利用できるデータベースサーバで、マルチバイト文字を認識しないエスケープ関数を利用するとサーバ側では対処できないケースも存在する(なおPostgreSQL 8.3からは「'」のみがエスケープ文字となる)。
このため、完全な対策には正しい設定とコーディングが必要となる。文字エンコーディングベースのSQLインジェクション防止対策としては、表1のような項目が考えられる。
またデータベースモジュールが提供する文字列エスケープ関数が正しく動作するためには、クライアント文字エンコーディング設定をAPIで変更しなければ意味がないデータベースサーバの文字エンコーディングにShift JISを利用しているユーザは、特にこの点に注意してほしい。なおPostgreSQLはデータベース文字エンコーディングにShift JISをサポートしていない。
PHPのMySQLモジュールのmysql_query関数は、一度に1つのクエリのみを実行する。しかしUNIONクエリを利用して例えばユーザ情報など、ほかのテーブルのデータを盗んだり、UPDATE/DELETEクエリのWHERE句を細工してデータの改竄/テーブル全体を削除するといった攻撃を行える。
- 文字エンコーディングベースのSQLインジェクション防止対策
-
- 正しいデータベース文字エンコーディング使用する
- クライアント側の文字エンコーディングを変更する場合、データベースAPI(pg_set_client_encoding()、mysql_set_charset()など)を利用する
- 文字列エスケープに文字エンコーディングを正しく認識するエスケープ関数(pg_escape_string()、mysql_real_escape_string()など)をデータベース接続リソースも指定して利用する
- データベースサーバを文字エンコーディングベースのSQLインジェクション対策済みのバージョンにアップグレードする
- プリペアードクエリを利用する
- 行ってはならないコーディング
-
- バイナリかバイナリに近い文字エンコーディングの利用
- addslashes関数によるエスケープ
- mysql_escape_string関数によるエスケープ
- マルチバイト文字を認識できない文字列置換関数(str_replace/eregなど)によるエスケープ
- SQL文(SET client_encoding TO - PostgreSQL、SET NAMES - MySQL)によるクライアント文字エンコーディングの変更
表:正しい設定をコーディングのために
本格的なSQLインジェクション攻撃
SQLインジェクションを利用した攻撃には、データベースサーバの種類、テーブル名やフィールド名などの情報が必要だ。攻撃者にとってはエラーメッセージからこれらの情報が取得できれば効率的だ。
またエラーメッセージなどに必要な情報が表示されない場合でも、「ブラインドSQLインジェクション」と呼ばれる手法を用いることで、まったく構成を知らないデータベースのテーブル名/フィールド名を解析して攻撃することが可能だ。
多少のスキルがあれば、脆弱性があるWebシステムに対して「ブラインドSQLインジェクション」を行いデータを盗み出す事は難しくない。「カスタムデータベースでテーブル構成などは知られていないから」「エラーメッセージを表示していないから」などと安心はできない。
文字エンコーディングベースの攻撃はSQLインジェクションのみでは無い
PHP 5.2.5では、不正なUTF-8文字エンコーディングを利用してhtmlentities関数およびhtmlspecialchars関数によるエスケープを回避できるセキュリティホールが修正されている。
しかしこれは、マルチバイト文字列が正しく取り扱われていない一例に過ぎない。「マルチバイト文字を認識できない文字列置換関数(str_replace/eregなど)によるエスケープ」はHTMLなどに利用するとクロスサイトスクリプティングの脆弱性になる場合もあるので注意が必要だ。
セキュアなシステムには文字エンコーディングが正しい文字エンコーディングであるかを検証するコードが不可欠となる。PHPのmbstringモジュールには不正な文字エンコーディングをチェックするmb_check_encoding関数が用意されている。これはPHP 4.xの変更履歴には記載されていないものだが、PHP 4.4.3から利用可能だ。
なお、2008年1月3日にリリースされたPHP 4.xの最終版「PHP 4.4.8」ではmysql_set_charset関数がバックポートされていない。この関数はシステム構成によっては必須の関数となるため、mysql_set_charset関数が必要ない構成へ変更/関数をバックポート/PHP 5.2.5にアップグレード、のいずれかの措置が必要だ。
文字エンコーディングの検証だけでは文字エンコーディングを利用した攻撃を防ぐ事はできないが、環境によっては検証によって完全に攻撃を遮断することもできる。具体的には、不正な文字エンコーディングを検出した場合に不正な部分を除去(サニタイズ)するのではなく、不正なデータが送信された事をログに記録し、プログラムの実行を停止させることで実現する。
正しい文字エンコーディングであるか検証していれば、多くの文字エンコーディングベースのSQLインジェクションも防止でき、htmlentities/htmlspecialchars関数のバグの影響も受けない。
次回、1月25日に公開する第3回では「Ruby on Rails」の環境を使って、脆弱性への攻撃を体験してみよう。 タイトルへ戻る