本当は怖い文字コードの話

第7回Unicodeからの多対一の変換[前編]

文字コードが引き起こすセキュリティ上の問題として、もっとも興味深いもののひとつである、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関数による変換
UnicodeShift_JIS
¡U+00a1! 0x21
¢U+00a2 0x81 0x91
£U+00a3 0x81 0x92
¥U+00a5\ 0x5C
¦U+00a6| 0x7C
©U+00a9c 0x63
ªU+00aaa 0x61
«U+00ab 0x81 0xE1
¬U+00ac 0x81 0xCA
(Soft Hyphen)U+00ad- 0x2D
®U+00aeR 0x52
¯U+00af 0x81 0x50
²U+00b22 0x32
³U+00b33 0x33
µU+00b5μ 0x83 0xCA
·U+00b7 0x81 0x45
¸U+00b8 0x81 0x43
¹U+00b91 0x31
ºU+00bao 0x6F
»U+00bb 0x81 0xE2
ÀU+00c0A 0x41
ÁU+00c1A 0x41
ÂU+00c2A 0x41
ÃU+00c3A 0x41
ÄU+00c4A 0x41
ÅU+00c5A 0x41
ÆU+00c6A 0x41
ÇU+00c7C 0x43
ÈU+00c8E 0x45
ÉU+00c9E 0x45
ÊU+00caE 0x45
ËU+00cbE 0x45
ÌU+00ccI 0x49
ÍU+00cdI 0x49
ÎU+00ceI 0x49
ÏU+00cfI 0x49
ÐU+00d0D 0x44
ÑU+00d1N 0x4E
ÒU+00d2O 0x4F
ÓU+00d3O 0x4F
ÔU+00d4O 0x4F
ÕU+00d5O 0x4F
ÖU+00d6O 0x4F
ØU+00d8O 0x4F
ÙU+00d9U 0x55
ÚU+00daU 0x55
ÛU+00dbU 0x55
ÜU+00dcU 0x55
ÝU+00ddY 0x59
ÞU+00deT 0x54
ßU+00dfs 0x73
àU+00e0a 0x61
áU+00e1a 0x61
âU+00e2a 0x61
ãU+00e3a 0x61
äU+00e4a 0x61
åU+00e5a 0x61
æU+00e6a 0x61
çU+00e7c 0x63
èU+00e8e 0x65
éU+00e9e 0x65
êU+00eae 0x65
ëU+00ebe 0x65
ìU+00eci 0x69
íU+00edi 0x69
îU+00eei 0x69
ïU+00efi 0x69
ðU+00f0d 0x64
ñU+00f1n 0x6E
òU+00f2o 0x6F
óU+00f3o 0x6F
ôU+00f4o 0x6F
õU+00f5o 0x6F
öU+00f6o 0x6F
øU+00f8o 0x6F
ùU+00f9u 0x75
úU+00fau 0x75
ûU+00fbu 0x75
üU+00fcu 0x75
ýU+00fdy 0x79
þU+00fet 0x74
ÿU+00ffy 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 の間では多対一の変換が行われてしまいます。

Windows上でのUnicodeからISO-8859-1への変換

ISO-8859-1(Windows-1252)は、英語環境などで使われているエンコーディングです。これも実際にWC_NO_BEST_FIT_CHARSを指定せずにWideCharToMultiByte関数を使ってUnicodeからISO-8859-1へ変換した結果を表2に示しておきます。

表2 WC_NO_BEST_FIT_CHARSを指定しない場合のWideCharToMultiByte関数によるUnicode→ISO-8859-1への変換
UnicodeISO-8859-1
ĀU+0100A 0x41
āU+0101a 0x61
ĂU+0102A 0x41
ăU+0103a 0x61
ĄU+0104A 0x41
ąU+0105a 0x61
ĆU+0106C 0x43
ćU+0107c 0x63
ĈU+0108C 0x43
ĉU+0109c 0x63
ĊU+010aC 0x43
ċU+010bc 0x63
ČU+010cC 0x43
čU+010dc 0x63
ĎU+010eD 0x44
ďU+010fd 0x64
ĐU+0110D 0xD0
đU+0111d 0x64
ĒU+0112E 0x45
ēU+0113e 0x65
ĔU+0114E 0x45
ĕU+0115e 0x65
ĖU+0116E 0x45
ėU+0117e 0x65
ĘU+0118E 0x45
ęU+0119e 0x65
ĚU+011aE 0x45
ěU+011be 0x65
ĜU+011cG 0x47
ĝU+011dg 0x67
ĞU+011eG 0x47
ğU+011fg 0x67
ĠU+0120G 0x47
ġU+0121g 0x67
ĢU+0122G 0x47
ģU+0123g 0x67
ĤU+0124H 0x48
ĥU+0125h 0x68
ĦU+0126H 0x48
ħU+0127h 0x68
ĨU+0128I 0x49
ĩU+0129i 0x69
ĪU+012aI 0x49
īU+012bi 0x69
ĬU+012cI 0x49
ĭU+012di 0x69
ĮU+012eI 0x49
įU+012fi 0x69
İU+0130I 0x49
ıU+0131i 0x69
ĴU+0134J 0x4A
ĵU+0135j 0x6A
ĶU+0136K 0x4B
ķU+0137k 0x6B
ĹU+0139L 0x4C
ĺU+013al 0x6C
ĻU+013bL 0x4C
ļU+013cl 0x6C
ĽU+013dL 0x4C
ľU+013el 0x6C
ŁU+0141L 0x4C
łU+0142l 0x6C
ŃU+0143N 0x4E
ńU+0144n 0x6E
ŅU+0145N 0x4E
ņU+0146n 0x6E
ŇU+0147N 0x4E
ňU+0148n 0x6E
ŌU+014cO 0x4F
ōU+014do 0x6F
ŎU+014eO 0x4F
ŏU+014fo 0x6F
ŐU+0150O 0x4F
őU+0151o 0x6F
ŔU+0154R 0x52
ŕU+0155r 0x72
ŖU+0156R 0x52
ŗU+0157r 0x72
ŘU+0158R 0x52
řU+0159r 0x72
ŚU+015aS 0x53
śU+015bs 0x73
ŜU+015cS 0x53
ŝU+015ds 0x73
ŞU+015eS 0x53
şU+015fs 0x73
ŢU+0162T 0x54
ţU+0163t 0x74
ŤU+0164T 0x54
ťU+0165t 0x74
ŦU+0166T 0x54
ŧU+0167t 0x74
ŨU+0168U 0x55
ũU+0169u 0x75
ŪU+016aU 0x55
ūU+016bu 0x75
ŬU+016cU 0x55
ŭU+016du 0x75
ŮU+016eU 0x55
ůU+016fu 0x75
ŰU+0170U 0x55
űU+0171u 0x75
ŲU+0172U 0x55
ųU+0173u 0x75
ŴU+0174W 0x57
ŵU+0175w 0x77
ŶU+0176Y 0x59
ŷU+0177y 0x79
ŹU+0179Z 0x5A
źU+017az 0x7A
ŻU+017bZ 0x5A
żU+017cz 0x7A
ƀU+0180b 0x62
ƉU+0189D 0xD0
ƑU+0191ニ・ 0x83
ƗU+0197I 0x49
ƚU+019al 0x6C
ƟU+019fO 0x4F
ƠU+01a0O 0x4F
ơU+01a1o 0x6F
ƫU+01abt 0x74
ƮU+01aeT 0x54
ƯU+01afU 0x55
ưU+01b0u 0x75
ƶU+01b6z 0x7A
ǀU+01c0| 0x7C
ǃU+01c3! 0x21
ǍU+01cdA 0x41
ǎU+01cea 0x61
ǏU+01cfI 0x49
ǐU+01d0i 0x69
ǑU+01d1O 0x4F
ǒU+01d2o 0x6F
ǓU+01d3U 0x55
ǔU+01d4u 0x75
ǕU+01d5U 0x55
ǖU+01d6u 0x75
ǗU+01d7U 0x55
ǘU+01d8u 0x75
ǙU+01d9U 0x55
ǚU+01dau 0x75
ǛU+01dbU 0x55
ǜU+01dcu 0x75
ǞU+01deA 0x41
ǟU+01dfa 0x61
ǤU+01e4G 0x47
ǥU+01e5g 0x67
ǦU+01e6G 0x47
ǧU+01e7g 0x67
ǨU+01e8K 0x4B
ǩU+01e9k 0x6B
ǪU+01eaO 0x4F
ǫU+01ebo 0x6F
ǬU+01ecO 0x4F
ǭU+01edo 0x6F
ǰU+01f0j 0x6A
ɡU+0261g 0x67
ʹU+02b9' 0x27
ʺU+02ba" 0x22
ʼU+02bc' 0x27
˄U+02c4^ 0x5E
ˈU+02c8' 0x27
ˉU+02c9 0xAF
ˊU+02ca´ 0xB4
ˋU+02cb` 0x60
ˍU+02cd_ 0x5F
˚U+02da° 0xB0
̀U+0300` 0x60
́U+0301´ 0xB4
̂U+0302^ 0x5E
̃U+0303~ 0x7E
̄U+0304 0xAF
̅U+0305 0xAF
̈U+0308¨ 0xA8
̊U+030a° 0xB0
̎U+030e" 0x22
̧U+0327 0xB8
̱U+0331_ 0x5F
̲U+0332_ 0x5F
;U+037e; 0x3B
ΓU+0393G 0x47
ΘU+0398T 0x54
ΣU+03a3S 0x53
ΦU+03a6F 0x46
ΩU+03a9O 0x4F
αU+03b1a 0x61
βU+03b2s 0xDF
δU+03b4d 0x64
εU+03b5e 0x65
μU+03bcμ 0xB5
πU+03c0p 0x70
σU+03c3s 0x73
τU+03c4t 0x74
φU+03c6f 0x66
һU+04bbh 0x68
։U+0589: 0x3A
٪U+066a% 0x25
 U+2000  0x20
U+2001  0x20
U+2002  0x20
U+2003  0x20
U+2004  0x20
U+2005  0x20
U+2006  0x20
U+2010- 0x2D
U+2011- 0x2D
U+2017= 0x3D
U+2024 0xB7
U+2032' 0x27
U+2035` 0x60
U+2044/ 0x2F
U+2070° 0xB0
U+20744 0x34
U+20755 0x35
U+20766 0x36
U+20777 0x37
U+20788 0x38
U+207fn 0x6E
U+20800 0x30
U+20811 0x31
U+20822 0x32
U+20833 0x33
U+20844 0x34
U+20855 0x35
U+20866 0x36
U+20877 0x37
U+20888 0x38
U+20899 0x39
U+20a1 0xA2
U+20a4 0xA3
U+20a7P 0x50
U+2102C 0x43
U+2107E 0x45
U+210ag 0x67
U+210bH 0x48
U+210cH 0x48
U+210dH 0x48
U+210eh 0x68
U+2110I 0x49
U+2111I 0x49
U+2112L 0x4C
U+2113l 0x6C
U+2115N 0x4E
U+2118P 0x50
U+2119P 0x50
U+211aQ 0x51
U+211bR 0x52
U+211cR 0x52
U+211dR 0x52
U+2124Z 0x5A
U+2128Z 0x5A
U+212aK 0x4B
U+212bA 0xC5
U+212cB 0x42
U+212dC 0x43
U+212ee 0x65
U+212fe 0x65
U+2130E 0x45
U+2131F 0x46
U+2133M 0x4D
U+2134o 0x6F
U+2205O 0xD8
U+2212- 0x2D
U+2213± 0xB1
U+2215/ 0x2F
U+2216\ 0x5C
U+2217* 0x2A
U+2218° 0xB0
U+2219 0xB7
U+221av 0x76
U+221e8 0x38
U+2223| 0x7C
U+2229n 0x6E
U+2236: 0x3A
U+223c~ 0x7E
U+2248ヒ・ 0x98
U+2261= 0x3D
U+2264= 0x3D
U+2265= 0x3D
U+226a 0xAB
U+226b 0xBB
U+22c5 0xB7
U+2302| 0xA6
U+2303^ 0x5E
U+2310 0xAC
U+2320( 0x28
U+2321) 0x29
U+2329< 0x3C
U+232a> 0x3E
U+2500- 0x2D
U+2502| 0xA6
U+250c+ 0x2B
U+2510+ 0x2B
U+2514+ 0x2B
U+2518+ 0x2B
U+251c+ 0x2B
U+2524| 0xA6
U+252c- 0x2D
U+2534- 0x2D
U+253c+ 0x2B
U+2550- 0x2D
U+2551| 0xA6
U+2552+ 0x2B
U+2553+ 0x2B
U+2554+ 0x2B
U+2555+ 0x2B
U+2556+ 0x2B
U+2557+ 0x2B
U+2558+ 0x2B
U+2559+ 0x2B
U+255a+ 0x2B
U+255b+ 0x2B
U+255c+ 0x2B
U+255d+ 0x2B
U+255e| 0xA6
U+255f| 0xA6
U+2560| 0xA6
U+2561| 0xA6
U+2562| 0xA6
U+2563| 0xA6
U+2564- 0x2D
U+2565- 0x2D
U+2566- 0x2D
U+2567- 0x2D
U+2568- 0x2D
U+2569- 0x2D
U+256a+ 0x2B
U+256b+ 0x2B
U+256c+ 0x2B
U+2580 0xAF
U+2584_ 0x5F
U+2588| 0xA6
U+258c| 0xA6
U+2590| 0xA6
U+2591| 0xA6
U+2592| 0xA6
U+2593| 0xA6
U+25a0| 0xA6
U+263cツ、 0xA4
U+2758| 0x7C
 U+3000  0x20
U+3008< 0x3C
U+3009> 0x3E
U+300a 0xAB
U+300b 0xBB
U+301a[ 0x5B
U+301b] 0x5D
U+30fb 0xB7
U+ff01! 0x21
U+ff02" 0x22
U+ff03# 0x23
U+ff04$ 0x24
U+ff05% 0x25
U+ff06& 0x26
U+ff07' 0x27
U+ff08( 0x28
U+ff09) 0x29
U+ff0a* 0x2A
U+ff0b+ 0x2B
U+ff0c, 0x2C
U+ff0d- 0x2D
U+ff0e. 0x2E
U+ff0f/ 0x2F
U+ff100 0x30
U+ff111 0x31
U+ff122 0x32
U+ff133 0x33
U+ff144 0x34
U+ff155 0x35
U+ff166 0x36
U+ff177 0x37
U+ff188 0x38
U+ff199 0x39
U+ff1a: 0x3A
U+ff1b; 0x3B
U+ff1c< 0x3C
U+ff1d= 0x3D
U+ff1e> 0x3E
U+ff20@ 0x40
U+ff21A 0x41
U+ff22B 0x42
U+ff23C 0x43
U+ff24D 0x44
U+ff25E 0x45
U+ff26F 0x46
U+ff27G 0x47
U+ff28H 0x48
U+ff29I 0x49
U+ff2aJ 0x4A
U+ff2bK 0x4B
U+ff2cL 0x4C
U+ff2dM 0x4D
U+ff2eN 0x4E
U+ff2fO 0x4F
U+ff30P 0x50
U+ff31Q 0x51
U+ff32R 0x52
U+ff33S 0x53
U+ff34T 0x54
U+ff35U 0x55
U+ff36V 0x56
U+ff37W 0x57
U+ff38X 0x58
U+ff39Y 0x59
U+ff3aZ 0x5A
U+ff3b[ 0x5B
U+ff3c\ 0x5C
U+ff3d] 0x5D
U+ff3e^ 0x5E
_U+ff3f_ 0x5F
U+ff40` 0x60
U+ff41a 0x61
U+ff42b 0x62
U+ff43c 0x63
U+ff44d 0x64
U+ff45e 0x65
U+ff46f 0x66
U+ff47g 0x67
U+ff48h 0x68
U+ff49i 0x69
U+ff4aj 0x6A
U+ff4bk 0x6B
U+ff4cl 0x6C
U+ff4dm 0x6D
U+ff4en 0x6E
U+ff4fo 0x6F
U+ff50p 0x70
U+ff51q 0x71
U+ff52r 0x72
U+ff53s 0x73
U+ff54t 0x74
U+ff55u 0x75
U+ff56v 0x76
U+ff57w 0x77
U+ff58x 0x78
U+ff59y 0x79
U+ff5az 0x7A
U+ff5b{ 0x7B
U+ff5c| 0x7C
U+ff5d} 0x7D
U+ff5e~ 0x7E

UnicodeからISO-8859-1への変換の場合には、Shift_JISのときより非常に多くの文字が多対一で変換されていることが見て取れます。

たとえば、全角文字「A」⁠U+FF21)は0x41(A)に、添え字用の数字である「₀」⁠U+2080)「0」⁠0x30)に変換されています。

問題を引き起こす「似た文字への変換」

このような多対一での文字の変換によって引き起こされるセキュリティ上の問題の一例としては、以下のようなものが考えられます。

パストラバーサル

UnicodeからShift_JISへの変換では、U+00A5の円記号「¥」が0x5Cのバックスラッシュ「\」に変換されます(日本語環境では表示する字体として円記号が当てられていますが⁠⁠。

また、UnicodeからISO-8859-1への変換では、U+2216「∖」とU+FF3Cの全角のバックスラッシュ「\」が ともに0x5Cのバックスラッシュ「\」に、U+2044「⁄⁠⁠、U+2215「∕⁠⁠、U+FF0F「/」が0x2Fのスラッシュ「/」に変換されます。

0x5Cや0x2FはWindowsにおけるディレクトリの区切りとして使用する文字ですので、Unicodeにて文字列の検証を行ったあとにShift_JISやISO-8859-1へ変換される場合、変換後の文字列に0x5Cが混入し、パストラバーサルが発生する可能性があります。

クロスサイトスクリプティング

UnicodeからISO-8859-1への変換では、U+2329「〈⁠⁠、U+3008「〈⁠⁠、U+FF1C「<」といった文字が0x3C「に、また、U+02BA「ʺ⁠⁠、U+030E「̎⁠⁠、U+FF02「"」が0x22「"」に変換されます。

前述のパストラバーサル同様、Unicode にて文字列の検証を行ったあとにShift_JISやISO-8859-1へ変換される場合、変換後の文字列に0x3Cや0x22といったメタキャラクタが含まれることになりますので、クロスサイトスクリプティングを引き起こす可能性があります。

このように、

  1. Unicodeで文字列の検査を行う
  2. Unicodeから他のエンコーディングに変換
  3. 変換された文字列を使用する

といった順序のときに、本来であれば1.でフィルタリングされるべき文字が含まれてしまい、セキュリティ上の問題点を引き起こすというのが「多対一の変換」による攻撃の一般的なシナリオとなります。

次回は、この多対一の変換が引き起こしたソフトウェアの脆弱性の具体例を挙げながら、対策方法などについても説明したいと思います。

おすすめ記事

記事・ニュース一覧