岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

Ruby製Webアプリでの入力チェック手順

今回はRuby製Webアプリでの入力チェック手順について考えてみる。Ruby1.9を使い、Railsなどのフレームワークは使わない前提だ。

文字エンコーディングの妥当性検証こそが重要

徳丸浩さんの『体系的に学ぶ 安全なWebアプリケーションの作り方』には「入力処理」として下記3つの処理が挙げられている。

(a)文字エンコーディングの妥当性検証
(b)文字エンコーディングの変換(必要な場合のみ)
(c)パラメータ文字列の妥当性検証

このうち(c)は単なる書式チェック(文字種や長さなどのチェック)なので、アプリの要件にしたがって粛々と行えばよいだろう。

(b)も必要な場合にだけ行えばよいので、さほど重要ではない。

重要なのは(a)で、これをいかにRubyで実現するかだ。同書では(おそらく意図的に)Rubyへの言及はなかった。

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 … 技術評論社