なぜPHPアプリにセキュリティホールが多いのか?

第21回文字エンコーディングとセキュリティ(3)

前回に引き続き、今回も文字エンコーディングとセキュリティをテーマに解説します。前回は壊れた文字エンコーディングを利用した攻撃、文字エンコーディングを誤認識させる攻撃を紹介しました。今回はSJIS特定の問題を簡単に紹介します。

文字エンコーディングのエスケープ方式を利用する方法

ほとんどの文字エンコーディングは、マルチバイト文字の中に⁠\⁠などの特殊文字を含みません。しかし、例外があります。Shift_JISでは⁠\⁠がマルチバイト文字に含まれるので、これが原因で脆弱性が発生する場合あります。

SJISの⁠表”
<?php echo rawurlencode(mb_convert_encoding('表', 'SJIS', 'UTF-8')); ?>
出力
%95%5C

“%5C⁠⁠\⁠です。⁠\⁠は文字列のエスケープなどに利用される特殊文字です。addslashes関数は、マルチバイト文字列の処理を考慮していないので、マルチバイト文字中の⁠\⁠は\でエスケープされます。その結果、⁠表⁠⁠表\⁠となってしまいます。

PHPユーザの場合、php.iniでmagic_quotes_gpc=on(すべての入力にaddslashesを適用するオプション)と設定されているシステムも多いので、SJISエンコーディングを使っている場合に不必要な⁠\⁠が現れて困った方も少なくないと思います。これは、マルチバイト文字の中に⁠\⁠が含まれていることが原因で発生している問題です。

一見この動作は無害のように思えるかもしれませんが、PHPやJavascriptなど、⁠\⁠がエスケープ文字になっているシステムでは重大な問題となります。

alert("<?php echo addslashes($name) ?>" + "さんは" + "<?php echo addslashes($age) ?>" + "歳ですね?");

のようなコードで、$name、$ageがユーザ入力値である場合、正しくバリデーションされていないと

alert("1234表\" + "さんは" + "悪意のあるデータ ……

※下線部がそれぞれ$name、$ageの値

のような攻撃が可能になり、不正なJavaScriptやHTMLを埋め込まれてしまいます。

MySQLや古いPostgreSQLの場合も同様の攻撃が可能でした。MySQLは現在でも文字列のエスケープに⁠\⁠が利用可能です。PostgreSQL 8.X以降では文字列のエスケープ文字は⁠'⁠(シングルクオート)になっています。文字列中の⁠'⁠(シングルクオート)は、シングルクオートでエスケープして⁠''⁠とします[1]⁠。

対策:

文字列をエスケープする場合、マルチバイト文字列と文字エンコーディングを考慮したエスケープ関数を利用する。

ereg関数、addslashes関数、mysql_escape_string関数はマルチバイト文字列や文字エンコーディングを考慮していません。通常の文字列関数もマルチバイト文字例を処理できません。

現在は対策済みですが、古いライブラリ(2005年春頃より古いライブラリ)を利用しているmysql_real_escape_string関数やpg_escape_string関数もマルチバイト文字列と文字エンコーディングを正しく考慮したエスケープ処理をしていません。古いシステムでは脆弱性が残ったままである場合も少なくないので注意が必要です。

SJISに対する攻撃

SJISに対しては今まで紹介した脆弱性と異なる脆弱性が発生する場合があります。アプリエーションが不正な文字エンコーディングをチェックせず、不適切なエスケープ処理を行っていると、文字列の出力先のシステムが不正な文字エンコーディングをチェックしていてもまったく役に立たなくなります。

最近のPostgreSQLやMySQLは、文字エンコーディングを利用した攻撃への対策が施され、文字列型データの場合、文字エンコーディングが正しくないとエラーとなります。しかし、SJISを利用している場合、文字エンコーディングのバリデーション不足と不適切なエスケープ処理が重なると、生成されたSQL文に不正な文字エンコーディングが含まれないため、攻撃をまったく検出できません。

また⁠表⁠を使った例を紹介します。⁠表⁠はURLエンコードすると⁠%95%5C⁠ですが、攻撃者は⁠%95%27⁠を利用します。⁠%27⁠はASCIIの⁠'⁠(シングルクオート)です。

“%95%27⁠をaddslashes関数やmysql_escape_string関数でエスケープすると⁠%95%5C%27⁠となり、SJISでは⁠表'⁠となります。つまり、%95%27の後に攻撃用の文字列を挿入でき、不正な文字エンコーディングも無いため、サーバ側で正規のクエリなのか、不正なクエリなのかまったく判別できません。

MySQLサーバへの攻撃例
SELECT * FROM tbl WEHRE fld = '\'悪意のある文字列が続く ……

最近では中国語もサポートしたアプリケーションを作る方も少なくないと思いますが、BIG5などSJISと同様の仕組みを持つアジアの文字エンコーディングでは同様の脆弱性が発生する可能性があります。

この攻撃には不正な文字エンコーディングが必要であるため、既に解説済みの「すべての文字列の文字エンコーディングが正しい文字エンコーディングであるか確認する」対策を取っていれば防御できます。しかし、マルチバイト文字を考慮しないエスケープ関数で余分な文字が追加されると問題が発生します。余分な⁠\⁠が追加されると、入力も処理結果も正しい文字エンコーディング、出力結果も正しい文字エンコーディング、となっても攻撃が可能となることもあります。

問題を防ぐには、マルチバイト文字の処理にはマルチバイト文字に対応した関数を必ず利用しなければなりません。

まとめ

SJISを利用していると、壊れた文字エンコーディングを利用しなくても、不適切なエスケープ処理が行われていると攻撃されてしまうことを理解頂けたと思います。SJISだけが危険な文字エンコーディングとは言えませんが、別のリスクがあることは理解していたほうがよいでしょう。

おすすめ記事

記事・ニュース一覧