前回は、WindowsにおいてWideCharToMultiByte APIを使用してUnicodeからShift_JISやISO-8859-1へ変換した場合に、WC_NO_BEST_FIT_CHARSというフラグを指定しなかった場合は「似ている文字への変換」が発生するため、セキュリティ上の問題が発生する可能性がある、という説明をしました。
今回は、実際にUnicodeから他の文字コードへの変換が、具体的に脆弱性を引き起こした例をいくつか紹介します。
電子メールの添付ファイル
電子メールの添付ファイル名には自由にUnicodeの文字が指定できますが、いくつかのメールクライアントにおいては添付ファイル名をUnicodeではなくShift_JISとして扱うために、問題が発生していました。
いくつかのメールクライアントは、添付ファイル名をUnicodeからShift_JISに変換して開こうとしますので、添付ファイルにU+00A9のコピーライトマーク「©」を使った「©ON」やU+00A5の円記号「¥」を使った「¥..¥..¥windows¥win.ini」のようなファイル名を与えると、Shift_JISへの変換の結果ファイル名が「CON」というWindowsでの予約デバイス名や、「﹨..﹨..﹨windows﹨win.ini」(﹨は0x5Cのバックスラッシュすなわちディレクトリ区切り記号)となり、メールクライアントが固まる、あるいは既存のファイルを上書きして添付ファイルを展開してしまうという問題がありました。
検索エンジンのインデックス生成
検索エンジンであるHyper EstraierのWindows版は、インデックス対象のファイル名をShift_JISとして扱います。
このとき、NTFSやFAT32といったWindowsでの一般的なファイルシステム上ではファイル名はUnicodeで保存されていますので、ファイル名のUnicodeからShift_JISへの暗黙的な変換が発生します。攻撃者がたとえば共有ディレクトリなどにU+00A9のコピーライトマーク「©」を使った「©ON」やU+00A5の円記号「¥」を使った「¥..¥..¥windows¥win.ini」のような名前のファイルを事前に作成しておくと、それらをShift_JISに変換した「CON」「﹨..﹨..﹨windows﹨win.ini」といったファイルを検索対象としてインデックス登録してしまうため、インデックスの生成が終わらない、検索対象外のディレクトリのファイルをインデックスに追加してしまうといった問題がありました。
MySQL Connector/JにおけるSQLインジェクション
MySQL Connector/JのPrepared Statementはクライアントサイドとサーバサイドの2種類のPrepared Statementがあり、デフォルトではクライアントサイドのPrepared Statementが使用されます。クライアントサイドでのPrepared Statementではクライアント上で内部的にSQLが生成されますが、characterEncodingパラメータがShift_JISやEUC-JPに設定に設定されている場合やMySQL側でcharacter_set_server=cp932などに設定されている場合には、SQLの生成においてUnicodeからそれらの文字エンコーディングへの変換が発生します。
このとき、SQL内にU+00A5の円記号「¥」やU+20A9のウォン記号「₩」が含まれていると、0x5Cのバックスラッシュ「﹨」に変換されるため、SQLインジェクションが発生してしまうという問題がありました。
対策
このように、Unicodeから他の文字エンコーディングへの変換によって引き起こされる脆弱性というのは、さまざまなソフトウェアのさまざまな個所で発生しています。
気をつけなければいけないのは、「Unicodeから他のエンコーディングへの変換」が開発者も意識していない暗黙のうちに発生することがあるという点です。
たとえば、旧来のC言語のスタイルでargc/argvのメカニズムを使用してコマンドライン引数を取得した場合には、WindowsからUnicodeで渡されたコマンドライン引数がランタイムライブラリ内でShift_JISに変換されます。また、Unicodeをサポートせずに書かれたプログラムがディレクトリのファイルを列挙した場合にはANSIバージョンのFindFirstFileAが呼び出され、ファイルシステム上ではUnicodeで保存されているファイル名がAPI内部でShift_JISに変換されてプログラムに渡されます。
また、それ以外にも外部DLLの呼び出し時の引数の変換(たとえばVBにおけるDLLとの文字列の受け渡しやC#におけるDllImportでのCharSet=CharSet.AutoあるいはCharSet=CharSet.Ansiの指定など)、クリップボードからCF_TEXTを指定してのANSI文字列としてのテキストの取得など、プログラマが明示的に意識していない場合でもUnicodeからShift_JISへ変換される機会は多数あります。
これらの変換においては、WC_NO_BEST_FIT_CHARS を指定していない「似ている文字への変換」と同様の変換方法が使用されるため、脆弱性につながることになります。
前回も説明しましたが、
- Unicodeで文字列の検査を行う
- Unicodeから他のエンコーディングに変換
- 変換された文字列を使用する
という処理の流れににおいて、本来であれば1.でフィルタリングされるべき文字が含まれてしまうというのが問題ですので、根源的な対策としては「文字列を最初から最後までUnicodeとして扱い、他の文字エンコーディングへの変換を発生させない」というのが最も有効です。
あるいは、どうしても他のエンコーディングへの変換が必要となる場合には、文字列の検査の後に文字エンコーディングの変換が発生しないように、
- Unicodeから他のエンコーディングに変換
- 変換された文字列の検査を行う
- 変換された文字列を使用する
といった流れになるようにしなければいけません。