JPCERT コーディネーションセンター

MSC10-C. 文字の符号化 - UTF8 に関連する問題

UTF-8 は Unicode を表すための可変長の符号表現である。UTF-8 は、Unicode シンボルにもよるが、1文字につき1バイトから4バイトを使用する。UTF-8 には次のような性質がある。

一般に、プログラムはまず UTF-8 データの妥当性を検証した後に、他の検査を行うべきである。有効な UTF-8 バイト列を以下の表に示す。

コードポイント 1バイト目 2バイト目 3バイト目 4バイト目
U+0000..U+007F 00..7F
U+0080..U+07FF C2..DF 80..BF
U+0800..U+0FFF E0 A0..BF 80..BF
U+1000..U+CFFF E1..EC 80..BF 80..BF
U+D000..U+D7FF ED 80..9F 80..BF
U+E000..U+FFFF EE..EF 80..BF 80..BF
U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
U+100000..U+10FFFF F4 80..8F 80..BF 80..BF

UTF-8 は元々 Plan 9 の開発者によって作成されたが [Pike 93]、Plan 9 自身は文字セットの下位16ビットの範囲だけをサポートしている。一般に、"Unicode" システムの多くは、21 ビットの ISO 10646 コード領域全体ではなく、下位16ビットの範囲だけをサポートしている [ISO/IEC 10646:2012]。

セキュリティに関する問題

[Yergeau 98] には以下のように書かれている。

UTF-8 の実装者は、無効な UTF-8 シーケンスの取扱いに関してセキュリティ面への影響を考慮する必要がある。攻撃者が、UTF-8の構文上認められないオクテットの並びを使って UTF-8 パーサを攻撃するというのは大いにあり得る話だ。

UTF-8 符号化形式の入力に対するセキュリティチェックを行うパーサに、巧妙な形式のデータを渡すことにより、特定の無効なオクテットの並びを文字として解釈させる攻撃が考えられる。たとえば、1オクテットの並び 00 として符号化された null 文字は拒否するが、無効な2オクテットの並び C0 80 として符号化された null 文字は許可してしまうかもしれない。あるいは、2F 2E 2E 2F ("/../") というオクテットの並びは拒否するが、2F C0 AE 2E 2F という無効なオクテットの並びは許可してしまうという例も考えられる。

推奨される具体的な対策を以下に示す。

最短形式だけ受け付ける

UTF-8 の最短形式のみ許可すること。必要以上に長い符号化表現を許可してしまうデコーダは、潜在的に危険な入力の複数の表現に対応できないかもしれない。例えば次のような例が考えられる。

  1. プロセス A はセキュリティ検査を実施するが、非最短形式の UTF-8 かどうかの検査を行わない。
  2. プロセス B はプロセス A からバイト列を受け取り UTF-16 に変換する。この際、バイト列は最短形式でない可能性がある。
  3. UTF-16 テキストには、プロセス A で排除されるべき潜在的に危険な文字が含まれる可能性がある。このような UTF-8 の非最短形式表現を使った攻撃は、Microsoft IIS Web サーバなどの製品においてセキュリティ上の検証を迂回するために使われた。

Unicode 規格のCorrigendum #1:「UTF-8 最短形式」[Unicode 2006] では、Unicode規格バージョン3.0に対する変更として、非最短形式の解釈を禁止している。

無効な入力の処理

UTF-8 デコーダが無効な入力に遭遇したときの動作は一律には定められていない。無効なバイトシーケンスに遭遇した際に UTF-8 デコーダが取る動作の例を以下に示す。なお、これらの動作を実装する際にはセキュリティを考慮する必要があることに注意。

  1. 代替文字"U+FFFD"に置き換える。代替文字が利用出来ない場合は "?" などの「ワイルドカード」文字に置き換える。
  2. そのバイトを無視する(たとえば、検証前に無効なバイトを削除するなど。詳しくはUnicode Technical Report #36, 3.5 Deletion of Code Points を参照)。
  3. 別の文字コードとしてバイトを解釈する(ISO-8859-1 を使うことが多い。他のエンコーディング、たとえば Shift_JIS、ではクロスサイトスクリプティングを引き起こす可能性があり潜在的に危険であることが知られている)。
  4. 無効かどうかは関知せず UTF-8 文字列であるとしてデコードする。
  5. デコードを中止し、エラーを報告する。

John Viega の"Protecting Sensitive Data in Memory" [Viega 2003] から引用した以下の関数は、文字列中の無効な文字の並びを検出するが、非最短形式であっても拒否しない。文字列が正当な並びで構成されている場合は 1 を返し、それ以外の場合は 0 を返す。

int spc_utf8_isvalid(const unsigned char *input) {
  int nb;
  const unsigned char *c = input;

  for (c = input;  *c;  c += (nb + 1)) {
    if (!(*c & 0x80)) nb = 0;
    else if ((*c & 0xc0) == 0x80) return 0;
    else if ((*c & 0xe0) == 0xc0) nb = 1;
    else if ((*c & 0xf0) == 0xe0) nb = 2;
    else if ((*c & 0xf8) == 0xf0) nb = 3;
    else if ((*c & 0xfc) == 0xf8) nb = 4;
    else if ((*c & 0xfe) == 0xfc) nb = 5;
    while (nb-- > 0)
      if ((*(c + nb) & 0xc0) != 0x80) return 0;
  }
  return 1;
}
サロゲートの破損

個別のサロゲート文字や、いずれかが破損しているサロゲート文字列の符号化は禁止しなければならない。破損したサロゲート文字列は Unicode データとして無効であり、Unicode データに含まれた場合にあいまいさを招く。破損したサロゲート文字列は、データ転送の不良やアプリケーションの内部バグを示す場合が多い。また、脆弱性検査のために意図的に使われることもある。

リスク評価

UTF-8 で符号化されたデータを適切に処理しないと、データの完全性が失われたり、サービス運用妨害攻撃が引き起こされることがある。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

MSC10-C

P2

L3

自動検出

ツール

バージョン

チェッカー

説明

LDRA tool suite

V. 8.5.4

176 S
376 S

実装済み

関連ガイドライン
参考情報
翻訳元

これは以下のページを翻訳したものです。

MSC10-C. Character Encoding - UTF8 Related Issues (revision 65)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter