Hatena::ブログ(Diary)

葉っぱ日記 このページをアンテナに追加

2006-08-29

[] 第4回: Unicode 文字列の比較  第4回: Unicode 文字列の比較 - 葉っぱ日記 を含むブックマーク

すっかり間があいてしまいました。ごめんなさい。今回のテーマは Unicode 文字列の比較です。

文字列の比較には、プログラミング言語によっては s1 = s2 のように簡単な演算子で比較できる場合もありますし、strcmp や StrComp のような関数を呼び出す必要があるかも知れません。そんななかでも今回取り上げるのは、Windows API の CompareString です。実際には、Unicode 同士の比較ですので、CompareStringW ですね。

さて、CompareString 関数には第2引数 dwCmpFlags で比較の条件をいろいろ変更することができます。一般的によく使われそうなのは、大文字小文字を無視して文字列を比較するためのフラグ NORM_IGNORECASE でしょうか。

実際に、この NORM_IGNORECASE フラグを指定して a-zA-Z と一致する文字を探してみると、a と A のような明らかに大文字小文字の関係にある文字以外に、次のような文字が一致するとみなされることがわかります。

B (U+0042)b (U+0062)ʙ(U+0299)
G (U+0047)g (U+0067)ɢ(U+0262)
H (U+0048)h (U+0068)ʜ(U+029C)
I (U+0049)i (U+0069)ɪ(U+026A)
L (U+004C)l (U+006C)ʟ(U+029F)
N (U+004E)n (U+006E)ɴ(U+0274)
R (U+0052)r (U+0072)ʀ(U+0280)
Y (U+0059)y (U+0079)ʏ(U+028F)

また、日本語でのアプリケーション開発時に使うかも知れない NORM_IGNOREWIDTH を NORM_IGNORECASE と組み合わせて同様に a-zA-Z と一致する文字を探してみると、以下のような文字が一致するとみなされることがわかります。

B (U+0042)b (U+0062)ʙ(U+0299)
G (U+0047)g (U+0067)ɢ(U+0262)
H (U+0048)h (U+0068)ʜ(U+029C)
I (U+0049)i (U+0069)ɪ(U+026A)
L (U+004C)l (U+006C)ʟ(U+029F)
N (U+004E)n (U+006E)ɴ(U+0274)
N (U+004E)n (U+006E)(U+207F)
R (U+0052)r (U+0072)ʀ(U+0280)
Y (U+0059)y (U+0079)ʏ(U+028F)

大文字小文字だけでなく全角半角も同一視すると、"ⁿ" (U+207F) という文字の一致が増えました。

開発者が予想していない範囲で文字列の一致が発生した場合、文字列の比較がセキュリティ上の要となっているポイントを通過することで脆弱性となってしまう場合があります。

  • アプリケーション内ではユーザ名を判断し、 "root" または "ROOT" といったユーザ名に対し特別な権限を与えていたが、攻撃者が "ʀoot" というユーザ名を利用した。
  • Webアプリケーションで "expression" やその全角表現 "expression" を検出して XSS を予防していたが、攻撃者は "expʀessɪoⁿ" という攻撃パターンをインジェクションした。

セキュリティ上、重要な文字列を比較する場合には、文字列の比較に利用する手段がどのような文字を一致させるのかを完全に把握してその比較機能を利用するようにしましょう。

2006-03-29

[] 第3回: Unicode から ISO-8859-1 への変換 09:39  第3回: Unicode から ISO-8859-1 への変換 - 葉っぱ日記 を含むブックマーク

前回の予告では「ASCIIへの変換」と書いたのですが、英語版 Windows の標準の ANSIコードページ(コードページ1252)を対象に話を進めますので ASCII ではなく ISO-8859-1 です。

さて、ISO-8859-1 についても 前回前々回と同様に WC_NO_BEST_FIT_CHARS を指定せずに WideCharToMultiByte を使って Unicode から変換させてみましょう。「似たような文字」として変換される文字の一覧を示しておきます。文字が多いのと、charset=ISO-8859-1 になっているということで、日記とはわけてあります。

「似たような文字」に変換される文字の数が、Shift_JIS(コードページ932)のときに比べ圧倒的に多くなりますので、これにより引き起こされるセキュリティ上の問題も Shift_JIS (コードページ932) に比べ増加します。このような変換によって引き起こされる脆弱性の代表的な例としては、以下のようなものが考えられます。

ディレクトリトラバーサル
'∖' (U+2216)、'\' (U+FF3C) がコードページ1252への変換で 0x5C に変換されます。また、'⁄' (U+2044)、'∕' (U+2215)、'/' (U+FF0F) が '/' (0x3F) に変換されます。そのため、Unicode にて文字列の検証を行ったあとにコードページ1252へ変換される場合、変換後の文字列に 0x5C が混入し、ディレクトリトラバーサルが発生する可能性があります。
XSS、SQLインジェクション
Shift_JIS への変換と同様に、ŚČŔĬℙŤ (U+015A U+010C U+0154 U+012C U+2119 U+0164) や σℂℝℑ₧Ʈ (U+03C3 U+2102 U+211D U+2111 U+20A7 U+01AE) などの文字列は、全て "SCRIPT" に変換されます。また、アルファベットだけでなく '〈' (U+2329) や '〈' (U+3008)、'<' (U+FF1C) は '<' (0x3C) に、' ʺ ' (U+02BA) や ' ̎ ' (U+030E)、' " ' (U+FF02) は '"' (0x22) に変換されます。このように HTML や SQL のメタキャラクタに変換される文字も多数ありますので、Unicode にてこれらの文字を含む文字列の検証を行ったあとにコードページ1252へ変換された場合には、XSS やSQL インジェクションの原因となりえます。
予約デバイス名
Shift_JISのときと同様に、Unicode のままでは問題のないファイル名が、コードページ1252に変換されることにより予約デバイス名となり、サービス不能を引き起こすことがあります。例えば、変換後の結果が 'A' (0x41) になるような Unicode の文字は、'Ā' (U+0100)、'Ă' (U+0102)、'Ą' (U+0104)、'Ǎ' (U+01CD)、'Ǟ' (U+01DE)、'A' (U+FF21) の6文字となりますので、ĀUX や ĂUX、ǍUX といったファイル名は、コードページ1252に変換されると全て AUX という予約デバイス名となります。

このように、Unicode から変換した際に「似たような文字」に変換される文字はShift_JIS(コードページ932)のときに比べ圧倒的に多いこと、英語圏における Unicode の理解度が低いであろうことから、英語圏で作成されたアプリケーションを英語環境で動作させる場合に Unicode が引き起こす脆弱性というのは、潜在的には Shift_JIS の場合よりも多いのではないかと想像されます。

次回は Unicode 文字列の比較をテーマにしたいと思います。

今回参考にした記事:

2006-02-09

[] 第2回: Unicode から Shift_JIS への変換(その2) 17:51  第2回: Unicode から Shift_JIS への変換(その2) - 葉っぱ日記 を含むブックマーク

前回の続きの前に少しだけ。RIPさんの質問

ところで、今回の内容を読んで気になっているというか悩んでる事。

今回の文章中の「Unicode」は、厳密には「UTF-16」と理解していいのだろか?

Windows 上での Unicode の標準的なエンコーディングですので、UTF-16LE と考えてください。ただし、いちいち Unicode と UTF-16、UTF-16LE という表記を使い分けるのも面倒なのと、Microsoft の文書類のほとんどが Unicode という表記になっており、それとの整合を合わせるためにも、UTF-16 または UTF-16LE と明記しなければならない状況でない限りは今後も Unicode と書くことにします。

さて、前回WideCharToMultiByte を使用して Unicode を Shift_JIS に変換する場合に、WC_NO_BEST_FIT_CHARS フラグを設定しなければ「似た文字への変換」がなされる、という話をしました。今回は、その「似た文字への変換」が引き起こすセキュリティ上の問題点についての説明です。

円記号
Unicode から Shift_JIS への変換にて U+00A5(¥) の円記号が 0x5C(\) のバックスラッシュに変換されます。そのため、文字列のチェック後に Shift_JIS への変換処理がある場合、変換後の文字列に 0x5C が混入し、ディレクトリトラバーサルの発生やその他のメタ文字として扱われる可能性があります。
XSS
ヤマガタさんも書かれていますが、U+00DF U+00C7 U+00A9 U+00CE U+0050 U+00FE (ßÇ©ÎPþ)という Unicode の文字列は、Shift_JIS に変換されると "sCRIPT" になります。WAF などで単純に、Unicode 文字列の "<SCRIPT>" だけを検査しており、その後に Shift_JIS への変換が発生する場合、XSS が引き起こされる可能性があります。
予約デバイス名
U+00A9 U+00D8 U+00D1 (©ØÑ) のような文字列は、Shift_JIS に直すと "CON" という予約デバイス名に変換されます。このような文字列をファイル名として扱うアプリケーションでは DoS を引き起こすことがあります。特に「UTF-8 なファイル名を受け取り、Shift_JIS で保存する」メーラのようなアプリケーションでは注意が必要になります。

ちなみに、前回の記事に対して、ヤマガタさんも「“こんな変換なんて普通しないから大丈夫だよ”と勘違いされてしまいそう」と書かれていますが、勘違いしないでください。これらの「似た文字への変換」は、開発者も知らない暗黙のうちに発生します(なぜなら、前回も書いたようにほとんどのプログラムやミドルウェア、ライブラリなどで WC_NO_BEST_FIT_CHARS を指定せずに WideCharToMultiByte を呼び出しているからです)。

具体的には、argc/argv によるコマンドライン引数の取得時や、ウィンドウテキストやクリップボードの文字列を Ansi バージョンの API を利用して取得した場合、外部DLL呼び出し時の暗黙の変換(VBなど)、IIS の Request("param") によるパラメータの取得、FindFirstFileA を利用したファイルの列挙など…数え上げればキリがありません。

特に、FindFirstFileA を利用したファイルの列挙については、WideCharToMultiByte の説明の "Security Alert"の項には

For strings that require validation, such as file, resource and user names, always use the WC_NO_BEST_FIT_CHARS flag with WideCharToMultiByte. This flag prevents the function from mapping characters to characters that appear similar but have very different semantics.

と書かれているにも関わらず、WC_NO_BEST_FIT_CHARS を指定せずに変換した場合と同じマッピングで Unicode のファイル名が Shift_JIS のファイル名に変換されますので、注意が必要です。

次回は、Unicode から ASCII(コードページ1252) への変換についてです。

hanazukinhanazukin 2006/02/09 10:14 ぉ。バージョンアップだー(´▽`*)

yamagata21yamagata21 2006/02/09 13:25 「<font color=”YAMAGATA”>ヤマガタ</font>」なら緑じゃん。
 わざわざ黒くなるように調整しただろー!!(笑)

hasegawayosukehasegawayosuke 2006/02/09 13:29 21はアイデンティティだって言ってたじゃんw ほら、はてなのidもw

comikencomiken 2006/02/10 09:23 とうていフィクションとは思えないリアリティが(汗

hasegawayosukehasegawayosuke 2006/02/10 09:36 だって別にぶちきれたりしませんもんw

2006-02-08

[] 第1回: Unicode から Shift_JIS への変換(その1) 15:49  第1回: Unicode から Shift_JIS への変換(その1) - 葉っぱ日記 を含むブックマーク

Windows 上で Unicode を扱う場合に発生するセキュリティ上の問題点などについて不定期に書いていくことにします。以前の内容と重なる部分も多いですし、時間的にもどこまで書けるかわかりませんけれど…。

さて第1回目は、 Windows 上で Unicode を扱う際のもっとも基本とも言える WideCharToMultiByte を使用した Unicode から Shift_JIS (コードページ932)への変換についてです。

WideCharToMultiByte を使用する際に発生しやすい問題点は以下の2点です。

  • バッファサイズの指定ミスによるバッファオーバーフロー
  • Unicode から Shift_JIS への変換における多対一のマッピング

バッファサイズの指定ミスによるバッファオーバーフロー

変換前の Unicode の文字列は「文字数」で指定するのに対し(cchWideChar)、変換結果を受け取るバッファのサイズ(cbMultiByte)は「バイト単位」で指定しなければいけません。cchWideChar、cbMultiByte のどちらも終端のヌル文字を含めたサイズを指定します。

cbMultiByte にゼロを指定した場合には、関数の戻り値としてバッファに必要な大きさが返されますので、それを利用して動的にメモリを確保するのもよいでしょう。

Unicode から Shift_JIS への変換における多対一のマッピング

WideCharToMultiByte の呼び出しにて、フラグ(dwFlags) に WC_NO_BEST_FIT_CHARS を指定していない場合、Unicode から Shift_JIS に変換する場合に複数の Unicode 文字が同じ文字に変換されることがあります。WC_NO_BEST_FIT_CHARS フラグは Windows 2000 および Windows 98 以降で有効となりますが、残念なことに WideCharToMultiByte に関する世の中の例題の多くではあまり指定されていないようです。

このフラグを指定した場合には、Unicode 文字に直接対応する Shift_JIS の文字がない場合には、既定の文字(デフォルトでは '?' )に変換されます。このフラグを指定している場合、Unicode と Shift_JIS は一対一に対応しますので、Shift_JIS をもう一度 Unicode に直した場合には(対応する文字がなく '?' に変換された場合を除き)同じ文字に戻すことができます。

フラグが指定されていない場合には、「よく似た文字」に変換されてしまい、そのような Shift_JIS の文字を再び Unicode に変換した場合には元の文字とは異なった文字に変換されてしまいます。例えば、U+00C0(À)は、Shift_JIS に変換すると 0x41(A) に置き換わります。もちろん、U+0041(A)も Shift_JIS への変換で 0x41(A) になります。逆に、Shift_JIS の 0x41 を Unicode に変換した場合には、U+0041(A) になります。このように、WC_NO_BEST_FIT_CHARS を設定しない変換においては、Unicode と Shift_JIS の間では多対一の変換が行われてしまいます。

具体的に、Shift_JIS に変換した場合に「似たような文字」として変換される文字は以下の通りです。次回はこれらの「似た文字の変換」によって引き起こされる問題について説明したいと思います。

UnicodeShift_JIS
¡U+00A1!0x21
¢U+00A2¢0x81 0x91
£U+00A3£0x81 0x92
¥U+00A5\0x5C
¦U+00A6|0x7C
©U+00A9c0x63
ªU+00AAa0x61
«U+00AB0x81 0xE1
¬U+00AC¬0x81 0xCA
­U+00AD-0x2D
®U+00AER0x52
¯U+00AF0x81 0x50
²U+00B220x32
³U+00B330x33
µU+00B5μ0x83 0xCA
·U+00B70x81 0x45
¸U+00B8 0x81
¹U+00B910x31
ºU+00BAo0x6F
»U+00BB0x81 0xE2
ÀU+00C0A0x41
ÁU+00C1A0x41
ÂU+00C2A0x41
ÃU+00C3A0x41
ÄU+00C4A0x41
ÅU+00C5A0x41
ÆU+00C6A0x41
ÇU+00C7C0x43
ÈU+00C8E0x45
ÉU+00C9E0x45
ÊU+00CAE0x45
ËU+00CBE0x45
ÌU+00CCI0x49
ÍU+00CDI0x49
ÎU+00CEI0x49
ÏU+00CFI0x49
ÐU+00D0D0x44
ÑU+00D1N0x4E
ÒU+00D2O0x4F
ÓU+00D3O0x4F
ÔU+00D4O0x4F
ÕU+00D5O0x4F
ÖU+00D6O0x4F
ØU+00D8O0x4F
ÙU+00D9U0x55
ÚU+00DAU0x55
ÛU+00DBU0x55
ÜU+00DCU0x55
ÝU+00DDY0x59
ÞU+00DET0x54
ßU+00DFs0x73
àU+00E0a0x61
áU+00E1a0x61
âU+00E2a0x61
ãU+00E3a0x61
äU+00E4a0x61
åU+00E5a0x61
æU+00E6a0x61
çU+00E7c0x63
èU+00E8e0x65
éU+00E9e0x65
êU+00EAe0x65
ëU+00EBe0x65
ìU+00ECi0x69
íU+00EDi0x69
îU+00EEi0x69
ïU+00EFi0x69
ðU+00F0d0x64
ñU+00F1n0x6E
òU+00F2o0x6F
óU+00F3o0x6F
ôU+00F4o0x6F
õU+00F5o0x6F
öU+00F6o0x6F
øU+00F8o0x6F
ùU+00F9u0x75
úU+00FAu0x75
ûU+00FBu0x75
üU+00FCu0x75
ýU+00FDy0x79
þU+00FEt0x74
ÿU+00FFy0x79
U+30940x83 0x94

RIPRIP 2006/02/08 16:35 文字コード話キターー(゜∀゜)ーー!

hasegawayosukehasegawayosuke 2006/02/08 16:42 (* ̄ー ̄)y−~~

rlyehrlyeh 2006/02/08 16:47 ひそかに100回予定ですよねと言ってみるテスト(w<どこまで書けるかわかりませんけれど…。

hasegawayosukehasegawayosuke 2006/02/08 16:48 (゜д゜;)

Ghetto(ゲットー)Ghetto(ゲットー) 2006/02/08 17:05 えっ?100テラ回予定ですよね?
とか言ってみたり・・・

hasegawayosukehasegawayosuke 2006/02/08 17:06 (;`Д´)

comikencomiken 2006/02/08 17:20 さすがに 100テラ回は辛そうですし、ここは間を取って…(100テラ+100)/2回ってことで (笑

hasegawayosukehasegawayosuke 2006/02/08 17:25 ヽ(`д´)ノ

こじまこじま 2006/02/09 11:30 ひゃくてらですか……
http://shop.kodansha.jp/bc/books/junrei/

ripjyrripjyr 2006/02/09 11:49 ひゃくてら、わらたwwwww

hasegawayosukehasegawayosuke 2006/02/09 11:51 ∩(´∀`)∩

通りすがり通りすがり 2006/02/10 15:08 > Unicode 文字に直接対応する Shift_JIS の文字がない場合には、既定の文字(デフォルトでは ’?’ )に変換されます。その場合、Shift_JIS をもう一度 Unicode に直した場合には同じ文字に戻すことができます。
? に変換された文字は、逆変換では元に戻らないと思うのですが。

hasegawayosukehasegawayosuke 2006/02/10 15:28 修正しました m(_ _)m

通りすがり2通りすがり2 2006/03/28 10:17 u+00b8 の所がtypoですね。81ではなくて 81 43に変換されます。
(81に変換されたら ¥ の無効化に使える別のセキュリティホールになりますね)

hasegawayosukehasegawayosuke 2006/03/28 10:21 おぉ。ありがとうございます<通りすがり2さん。
最初、Unicodeから2バイト文字に変換されるのを考慮してないプログラムで機械的に変換かけて後から手作業で修正したのでその漏れですね orz