Validating Strings

文字エンコーディングバリデーション を受けて。

この辺の話ははせがわさんがお詳しいので、ご存じでない方はまず「本当は怖い文字コードの話」をお読みください。

不正なバイト列を用いた攻撃について

さて、大垣さんの記事は

  • 不正なバイト列をWebブラウザに送ると攻撃が可能となる場合があるので、Webアプリケーション側で対策が必要
  • 不正なバイト列のチェックには文字エンコーディング変換を用いる
  • まだ対策が不十分な事が多いので、みんな対策しようね
  • HTTP charset パラメタの勧め *1

というお話。「validation」や「不正な」の対象が「文字エンコーディング」になっているのが気になります。不正なのはバイト列ですよね。invalid byte sequence を用いた攻撃の話です。

変換による検査

さて、これらの記事では「文字エンコーディング変換を利用したバリデーションをする方法」が不正なバイト列の validation に対する模範解答であるかのように書いているのですが、違うからね?

文字コード変換を不正なバイト列の検出に使えるかは実装依存です。使えない例 *2 としては nkf *3 とか Ruby/transcode *4 とか。使える例は iconv() *5 とか Perl/Encode *6 とか。

Ruby における手法

元記事にもある通り、Ruby 1.9 は String に対して validation 機能を持っていて、不正なバイト列を持つ String に対して正規表現マッチとの操作をすると例外が上がるようになっています。

Ruby 1.8 にはこのような機能はありません。kconv に String#isutf8 や String#issjis のように、あるエンコーディングとして正しいかを調べられるメソッドはあったりします。また、UTF-8 は /\A[\w\W]\z/ =~ str でチェックができ・・・そう?*7 UTF-8 限定ならば String#unpack("U*") も利用できます。これでは、冗長な UTF-8 を見つけた場合は ArgumentError: redundant UTF-8 sequence が、多バイト文字の一部があった場合は ArgumentError: malformed UTF-8 character が上がります。

しかし、このレベルの話は言語やフレームワークのレベルで面倒をみるべきな気もするんですよね。とか考えると、1.9 では現在不正な String に対して文字列の結合を行う場合等は例外が上がらないのだけれど、あらゆる操作で例外が上がるようになった方がいいんだろうか。

ベンチマーク

高井さんベンチマークをとってくださいました。見ると、$KCODE="u";/\A[\w\W]\z/ =~ str が圧倒的に速いですね。この方法は UTF-8 でしかも $KCODE を設定しないといけない事に改めて注意。

*1:原本 が繋がらないので Internet Archive

*2:正確には validater として使うのにコツがあるもの

*3:不正なバイト列があってもエラーにならない上に、デフォルトで正規化や MIME 出コードが走る。これを回避する方法はここでは書かない

*4:変換元と変換先のエンコーディングが同じだと変換処理が走らない

*5:リンク先の仕様にある通り、不正なバイト列があると止まる。実装が仕様通りならばだけどね。glibc iconv や GNU libiconv では変換先に文字がない場合も止まる独自仕様なことに注意

*6:デフォルトでは置換文字に変換であることに注意、あと Web における Shift_JISWindows-31J のことであることをわすれずに

*7:[\w\W] の代わりに . を使ったり、 UTF-8 以外のエンコーディングでは期待通り動かないことに注意