第4回: Unicode 文字列の比較

すっかり間があいてしまいました。ごめんなさい。今回のテーマは Unicode 文字列の比較です。
文字列の比較には、プログラミング言語によっては s1 = s2 のように簡単な演算子で比較できる場合もありますし、strcmp や StrComp のような関数を呼び出す必要があるかも知れません。そんななかでも今回取り上げるのは、Windows APICompareString です。実際には、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ⁿ" という攻撃パターンをインジェクションした。

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