[1]。
- UTFエンコーディングの種類
- UTF-7, UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE
日本語の文字エンコーディングにはUTF以外にJIS(ISO-2022-JP)、Shift-JIS、EUCがありますが、基本となっている文字コードはおおよそ一緒です。利用している文字コードはASCII、JIS X 0201、JIS X 0208、JIS X 0212、JIS X 213などです。
- 参考:文字コード研究 (ラトルズ) ISBN4-89977-051-0
不正な文字エンコーディングを利用した攻撃
日本のWebサイトで利用されている文字エンコーディングは、すべてマルチバイト文字です。すべてのマルチバイト文字エンコーディングは、不正な文字エンコーディングによる攻撃のリスクがあると考えて構いません。
不正な文字エンコーディングを利用した攻撃には、本来必要なバイト数未満のデータを送り、特殊文字を無効化する方法が利用されます。文字列の終端を表す「"」(ダブルクオート)、「'」(シングルクオート)を無効化し、JavaScriptインジェクション/HTMLインジェクション/XMLインジェクション/SQLインジェクションを行います。
不正な文字エンコーディングを利用した攻撃では、本来の長さより短いデータや、正しいエンコーディングでは有り得ないデータを送信させ、そのデータを受け取ったプログラムを誤作動させる攻撃です。Webアプリケーションではブラウザやプラグインを誤作動させたり、Webアプリケーションがサーバ側で利用しているSQLデータベース、XMLデータベースなどを誤作動させます。結果として、不正なJavaScriptの実行や不正なSQL文の実行を可能にします。
PHPのXML関連モジュールに利用されているlibxml2も壊れた文字エンコーディングに対する脆弱性が何度も発見されています。壊れた文字エンコーディングがあるとXMLインジェクションが可能となったり、サービス不能攻撃が可能になるケースがありました。PHPのHTMLエスケープ関数であるhtmlentities/htmlspecialcharsも壊れた文字エンコーディングに対する複数の脆弱性が発見されています(PHP 4.4.9には脆弱性が一部残されています)。Webアプリケーションが壊れた文字エンコーディングを利用した攻撃に対して脆弱でなくても、出力先のWebブラウザが脆弱では意味が有りません。壊れた文字エンコーディングを利用した攻撃は大きな脅威と言えます。
対策:
入力時のバリデーション処理で、すべての文字列に対してエンコーディングが正しいかチェックする。
PHPで文字エンコーディングが正しいかチェックする場合、mb_check_encoding関数を利用します。mb_check_encoding関数は文字列型のみチェックするので、$_GET、$_POST、$_COOKIE、$_SERVERなどをチェックする場合、こられらは配列も含むのでarray_walk_recursive関数と一緒に使います。
この攻撃に対する対策は非常に簡単ですが、ほとんどのアプリケーションは未対策です。特に海外製のPHPアプリケーションはほぼすべて未対策である、と言ってよいくらい、文字エンコーディングが正しいかチェックしていません。
壊れた文字エンコーディングを検出していれば、htmlentities/htmlspecialchars関数のバグを利用したエスケープ処理を回避した攻撃も行えませんでした。libxml2の脆弱性にも、文字エンコーディングをチェックしていれば回避できた問題も多くありました。壊れた文字エンコーディングをチェックしていれば、不正な文字エンコーディングを利用たSQLインジェクションも回避できました。
不正な文字エンコーディングを利用したセキュリティ上の問題はどこで発生してもおかしくありません。文字エンコーディングが正しい文字エンコーディングであるかチェックするのは、Webアプリケーションのみでなく、すべてのアプリケーションに必須のセキュリティ対策だといえます。
文字エンコーディングをチェックしていないWebアプリケーションをそのまま利用するのはリスクが高いので、php.ini設定のauto_prepend_file設定などを利用し、文字エンコーディングをチェックするコードを追加して利用したほうが安全です。ただし、URLエンコードでバイナリデータを受け渡ししているアプリケーションは、auto_prepend_file設定を利用してすべての入力をチェックすると正常に動作しないので、除外リストを作成してバイナリデータは除外するようにします。
文字エンコーディングを誤認識させる(誤認識を利用する)攻撃
文字エンコーディングを誤認識させる方法は、クロスサイトスクリプティング脆弱性の危険性を指摘したCA-2000-02でも指摘しているのは既に紹介した通りです。もっとも分かりやすい例は、UTF-7エンコーディングを利用した攻撃方法です。UTF-7の場合、HTMLの特殊文字である<、>がエンコードされ、別の文字列に変換されます。
UTF-7に変換後には<、>がないので、UTF-7エンコーディングの文字列データをhtmlentities/htmlspecialchars関数などでエスケープ処理しても意味がありません。UTF-7はASCIIデータと変わらないので、不正な文字エンコーディングを検出していても役に立ちません。UTF-7以外で記述されたページに、UTF-7の文字列データを送り、ブラウザなどが誤ってUTF-7文字エンコーディングと認識してまうと不正なスクリプトやHTMLタグの埋め込みが可能となります。
Webブラウザには文字エンコーディングの自動識別機能があります。そして、多くのユーザは文字エンコーディングの自動識別機能を有効に設定しています。このため、攻撃者はWebアプリケーションにUTF-7でエンコードした文字列を送るだけで、簡単に攻撃が成功してしまう場合もあります。
2000年のクロスサイトスクリプティング脆弱性のアドバイザリ(CA-2000-02)で「すべての動的に生成されたWebページは明示的に適切な文字コードセットを設定しなければならない」としているのは、この種の攻撃を防ぐために記述されています。
対策:
明示的に文字エンコーディングを指定する。例えば、HTTPヘッダのcharset属性でアプリケーションが利用する文字エンコーディングを指定する。
HTTPヘッダで文字エンコーディングを設定している場合、本来であればWebブラウザはエンコーディングの自動識別を行うべきではありません。しかし、自動識別を行っているブラウザではHTTPヘッダの指定を無視して、自動的に文字エンコーディングを識別してしまうブラウザもあります。追加の対策としてMETAタグでも文字エンコーディングを指定するほうがよいでしょう。
PHPでHTTPヘッダのContent-Typeヘッダのcharset属性で文字エンコーディングを指定する場合は、ini_set関数でdefault_charsetを設定するか、php.iniのdefault_charsetを設定します。
JSPやASPでWebアプリケーションを作ったことがある方は、お約束として文字エンコーディングを記載するようになっているサンプルコードが多いです。その結果として正しい文字エンコーディングの取り扱いとなっているアプリケーションが多いですが、PHPの場合、php.iniで設定できるのでアプリケーションで設定していないことが少なくありません。
筆者はphp.iniファイルの規定値としてdefault_charsetに何らかの文字エンコーディングを指定すべきだと考えていますが、ソース版のPHPに付属するphp.iniにはdefault_charsetが設定されていません。多くのPHPアプリケーションがdefault_chaset設定を変更していないので、php.iniで設定するか、アプリケーションの初期化ファイルなどに手を加えない限り脆弱な状態になってしまっています。
文字エンコーディングの誤認識を利用した攻撃を防ぐには、先ほど紹介した対策である「文字エンコーディングが正しいエンコーディングかチェックする」対策も重要です。Webサイトを参照したユーザが「文字化けかな?」と思って、使用する文字エンコーディングを切り替えた時に不正なJavaScriptを実行させる攻撃手法もよく知られています。
ブラウザの文字エンコーディングの自動認識機能を無効にしてWebサイトを利用していると、比較的有名なサイトでも文字エンコーディングの取り扱いに不備があることに気が付くことが少なくありません。この記事を読まれる方は開発者の方とは思いますが、普段Webページを参照するエンドユーザとしてのセキュリティ対策として「ブラウザの文字エンコーディング自動認識機能は無効にする」ことをお薦めします。より安全にブラウジングできるだけでなく、自分の構築しているサイトが誤った処理をしていないかチェックするためにも役立ちます。
まとめ
すべての文字エンコーディングを利用した攻撃については解説できませんでしたが、文字エンコーディングと文字列を厳格に取り扱う必要性は理解頂けたと思います。次回も文字エンコーディングを用いた代表的な攻撃の手法の解説を続けます。