Ruby製Webアプリでの入力チェック手順
今回はRuby製Webアプリでの入力チェック手順について考えてみる。Ruby1.9を使い、Railsなどのフレームワークは使わない前提だ。
文字エンコーディングの妥当性検証こそが重要
徳丸浩さんの『体系的に学ぶ 安全なWebアプリケーションの作り方』には「入力処理」として下記3つの処理が挙げられている。
(a)文字エンコーディングの妥当性検証
(b)文字エンコーディングの変換(必要な場合のみ)
(c)パラメータ文字列の妥当性検証
このうち(c)は単なる書式チェック(文字種や長さなどのチェック)なので、アプリの要件にしたがって粛々と行えばよいだろう。
(b)も必要な場合にだけ行えばよいので、さほど重要ではない。
Rubyでは(b)→(a)→(c)
RubyのStringには、String#valid_encoding? というメソッドがある。これが(a)に使えるはずだ。ただ、このメソッドにはチェックに使うエンコーディングが渡せない。文字列自体のエンコーディングがチェックに使われる。
前回の記事ではRackの挙動に触れたが、Rackを使おうと使うまいと、入力文字列のエンコーディングがWebアプリ側の求めるものと同一である保証はない。よって String#valid_encoding? の前に(b)を行う必要がある。
(b)には String#encode や Encoding::Converter#convert が使える。結論として、Rubyでの入力チェック例は下記のようになる。
# (b)文字エンコーディングの変換 s = input_string.encode(Encoding::UTF_8) # (a)文字エンコーディングの妥当性検証 raise 'Bad Request' unless s.valid_encoding? # (c)パラメータ文字列の妥当性検証 ...
UTF-8を選んだのはあくまで例で、他のエンコーディングでも処理順は変わらない。
追記(2011-07-12)
状況によっては、String#encode や Encoding::Converter#convert ではなく String#force_encoding が適切な場合もあるだろう。バイトの並びは変えずにエンコーディング情報だけを変えたい場合だ。そのとき、Rubyでの入力チェック例は下記のようになる。
# (b)文字エンコーディングの変換 input_string.force_encoding(Encoding::UTF_8) # (a)文字エンコーディングの妥当性検証 raise 'Bad Request' unless input_string.valid_encoding? # (c)パラメータ文字列の妥当性検証 ...
String#valid_encoding? は信頼できるか
気になるのは String#valid_encoding? の信頼性だ。UTF-8についてざっと確認したかぎりでは、malformedなバイト列、冗長表現、U+110000以降、および、サロゲートがfalseとなった。ここまで対応できているならば信頼してもいいだろう。万一バグが見つかったとしてもレポートすればいいだけだ。
現在のほとんどのOSやライブラリ,フレームワークなどのミドルウェアでは,このような冗長なUTF-8表現は禁止されていると考えられます。そのため,冗長なUTF-8による検査の漏れを防ぐもっとも最善の方法は,UTF-8の検査や他の符号化形式への変換をライブラリやフレームワークに任せ,「自前でUTF-8を処理しない」ということに尽きます。
本当は怖い文字コードの話:第4回 UTF-8の冗長なエンコード|gihyo.jp … 技術評論社