文字コードが引き起こすセキュリティ上の問題として,もっとも興味深いもののひとつである,Unicodeから他の文字コードへの「多対一の変換」で引き起こされる問題点について,今回と次回で説明します。
ご存じのとおり,Unicodeには非常に多数の文字が収録されていますが(現在最新版のUnicode 5.1.0では100,713文字が収録されているそうです),Unicodeから他の文字コードへの変換においては,互換性や可読性の維持のためか,複数のUnicodeの文字が他の文字コードでは単一の文字に変換されることがあります。
この「多対一」の変換が,開発者も想定していなかったような問題を引き起こす原因となることが多々あります。
具体的な例として,Windows上でのUnicodeからの変換について説明します。
Windows上でのUnicodeからShift_JISへの変換
Windows上での文字列のUnicode(UTF-16LE)からShift_JISへの変換においては,多くの場合APIのWideCharToMultiByteが使用されます。
このWideChatToMultiByte関数は,2番目の引数にWC_NO_BEST_FIT_CHARSというフラグを指定することができます。
WC_NO_BEST_FIT_CHARSを指定した場合,Unicode文字に対応するShift_JISの文字が存在しない場合には,特定の文字(デフォルトでは '?')に変換されます。
WC_NO_BEST_FIT_CHARSが指定されていない場合,Unicode文字に対応するShift_JISの文字が存在しない場合には,「よく似た文字」に変換されてしまいます。
WC_NO_BEST_FIT_CHARSを指定せずにWideCharToMultiByte関数を実際に呼び出した場合の文字の変換例を,表1に示しておきます。
表1 WC_NO_BEST_FIT_CHARSを指定しない場合のWideCharToMultiByte関数による変換
| Unicode | Shift_JIS | ||
|---|---|---|---|
| ¡ | U+00a1 | ! | 0x21 |
| ¢ | U+00a2 | ¢ | 0x81 0x91 |
| £ | U+00a3 | £ | 0x81 0x92 |
| ¥ | U+00a5 | \ | 0x5C |
| ¦ | U+00a6 | | | 0x7C |
| © | U+00a9 | c | 0x63 |
| ª | U+00aa | a | 0x61 |
| « | U+00ab | ≪ | 0x81 0xE1 |
| ¬ | U+00ac | ¬ | 0x81 0xCA |
| (Soft Hyphen) | U+00ad | - | 0x2D |
| ® | U+00ae | R | 0x52 |
| ¯ | U+00af |  ̄ | 0x81 0x50 |
| ² | U+00b2 | 2 | 0x32 |
| ³ | U+00b3 | 3 | 0x33 |
| µ | U+00b5 | μ | 0x83 0xCA |
| · | U+00b7 | ・ | 0x81 0x45 |
| ¸ | U+00b8 | , | 0x81 0x43 |
| ¹ | U+00b9 | 1 | 0x31 |
| º | U+00ba | o | 0x6F |
| » | U+00bb | ≫ | 0x81 0xE2 |
| À | U+00c0 | A | 0x41 |
| Á | U+00c1 | A | 0x41 |
| Â | U+00c2 | A | 0x41 |
| Ã | U+00c3 | A | 0x41 |
| Ä | U+00c4 | A | 0x41 |
| Å | U+00c5 | A | 0x41 |
| Æ | U+00c6 | A | 0x41 |
| Ç | U+00c7 | C | 0x43 |
| È | U+00c8 | E | 0x45 |
| É | U+00c9 | E | 0x45 |
| Ê | U+00ca | E | 0x45 |
| Ë | U+00cb | E | 0x45 |
| Ì | U+00cc | I | 0x49 |
| Í | U+00cd | I | 0x49 |
| Î | U+00ce | I | 0x49 |
| Ï | U+00cf | I | 0x49 |
| Ð | U+00d0 | D | 0x44 |
| Ñ | U+00d1 | N | 0x4E |
| Ò | U+00d2 | O | 0x4F |
| Ó | U+00d3 | O | 0x4F |
| Ô | U+00d4 | O | 0x4F |
| Õ | U+00d5 | O | 0x4F |
| Ö | U+00d6 | O | 0x4F |
| Ø | U+00d8 | O | 0x4F |
| Ù | U+00d9 | U | 0x55 |
| Ú | U+00da | U | 0x55 |
| Û | U+00db | U | 0x55 |
| Ü | U+00dc | U | 0x55 |
| Ý | U+00dd | Y | 0x59 |
| Þ | U+00de | T | 0x54 |
| ß | U+00df | s | 0x73 |
| à | U+00e0 | a | 0x61 |
| á | U+00e1 | a | 0x61 |
| â | U+00e2 | a | 0x61 |
| ã | U+00e3 | a | 0x61 |
| ä | U+00e4 | a | 0x61 |
| å | U+00e5 | a | 0x61 |
| æ | U+00e6 | a | 0x61 |
| ç | U+00e7 | c | 0x63 |
| è | U+00e8 | e | 0x65 |
| é | U+00e9 | e | 0x65 |
| ê | U+00ea | e | 0x65 |
| ë | U+00eb | e | 0x65 |
| ì | U+00ec | i | 0x69 |
| í | U+00ed | i | 0x69 |
| î | U+00ee | i | 0x69 |
| ï | U+00ef | i | 0x69 |
| ð | U+00f0 | d | 0x64 |
| ñ | U+00f1 | n | 0x6E |
| ò | U+00f2 | o | 0x6F |
| ó | U+00f3 | o | 0x6F |
| ô | U+00f4 | o | 0x6F |
| õ | U+00f5 | o | 0x6F |
| ö | U+00f6 | o | 0x6F |
| ø | U+00f8 | o | 0x6F |
| ù | U+00f9 | u | 0x75 |
| ú | U+00fa | u | 0x75 |
| û | U+00fb | u | 0x75 |
| ü | U+00fc | u | 0x75 |
| ý | U+00fd | y | 0x79 |
| þ | U+00fe | t | 0x74 |
| ÿ | U+00ff | y | 0x79 |
| ゔ | U+3094 | ヴ | 0x83 0x94 |
この表を見るとわかるように,例えば発音記号のついた文字「À」(U+00C0)やU+00C1(Á)はShift_JISに変換すると「A」(0x41)に置き換わりますし,コピーライトマーク「©」(U+00A9)は 「c」(0x63)に置き換わります。
このように,WC_NO_BEST_FIT_CHARS を設定しない変換においては,Unicode と Shift_JIS の間では多対一の変換が行われてしまいます。

