2013年07月

入力値検証の目的は「ユーザビリティ」と「システムの信頼性」の向上

ゆうべ寝床で思いついた意見。さっそくブログに書こうと、誰か他に言っている人がいないか調べたら、徳丸本の76ページに思いっきり書いてあった。

すなわち、入力値検証の目的は以下の通りです。

  • 入力値の間違いを早期に発見して再入力を促すことにより、ユーザビリティ(使いやすさ)を向上する
  • 間違った処理を継続することによるデータの不整合などを防ぎ、システムの信頼性を向上させる

もう、これでFAじゃないですか。精読してない俺の馬鹿。

たとえば、ユーザ登録フォームでパスワードが未入力だった場合、アプリケーションは「登録処理に失敗しました」などという曖昧なものではなく「パスワードは必須です」のような明確なメッセージを返すべきだ。それがユーザビリティの向上。

DBにおかしな値が登録されないようにすべきなのは言わずもがな。Railsのドキュメントに「Validations are used to ensure that only valid data is saved into your database.」とある通り。これが信頼性の向上。


ちなみに、このドキュメントには、以下のようにも書かれている。

  • Model-level validations are the best way to ensure that only valid data is saved into your database.
  • Controller-level validations can be tempting to use, but often become unwieldy and difficult to test and maintain. Whenever possible, it's a good idea to keep your controllers skinny, as it will make your application a pleasure to work with in the long run.
  • It's the opinion of the Rails team that model-level validations are the most appropriate in most circumstances.

なので、Railsがコントローラでのバリデーションを推奨するようになったというのはデマです。


入力値のなかには、DBと無関係なものもありうる。「HTML文書のURLを入力するとtitle要素の文字列が返るAPI」を想像すると分かりやすいだろうか。入力値がURL形式であれば、そのURLにアクセスし、ドキュメントをパースするだけ。

こうした、DBと無関係な入力値は、コントローラでバリデーションしてもよいように思える。ただ、少なくともRailsの流儀では、ActiveModel::Modelをインクルードしたモデルでバリデーションするのが普通っぽい。まあ、いずれにしても趣味の問題だろう。


徳丸本の72ページに「文字エンコーディングの妥当性検証を行う理由は、文字コードを使った攻撃手法があるからです」とあるが、ここはしっくりこない。「入力値検証の一部」あるいは「入力値検証の前提」(文字エンコーディングの妥当性を検証しないと、入力値を正しく検証できない)という説明ではダメなんだろうか。

各種インジェクション攻撃やらディレクトリ・トラバーサル攻撃やらについては、それらが起きそうな場所で、文字エンコーディングの妥当性検証を行えばよいわけだし、それほど大変なイメージがいまだに持てないんですよね。しつこくてすみません。

大垣さんと徳丸さんのパリデーション論争の感想

いずれの論点においても、ぼくは徳丸さんのご主張に共感するものだけれど、論争に参戦し、大垣さんを説得しようとは思わない。だって無理だもの。

人生は短いので、無理なことは極力避けたい。ぼくが徳丸さんだったら、PHP入門書の執筆を優先させることだろう。

追記(2013-07-24)

はっきりいえば、徳丸さん(あるいは徳丸さんの主張に賛同するもの)の戦うべき相手は、大垣さんではなく、彼が拠り所としている団体や規格、つまり「世界の常識」そのものなんじゃないかと思うんですよね。それらを引っくりかえせば、大垣さんの主張は根拠がなくなる。

敵が大きすぎて手に負えない、ということなら、ひとまず戦いは諦めて、「世界の常識」を無思慮に信じてしまいかねないWebアプリ開発者(特に初心者)を地道に啓蒙していくしかないんじゃないか。

それには、多少センセーショナルなキャンペーンが必要かもしれない。「駄目なセキュリティ文書の見分け方・その1=ホワイトリスト方式を推奨しているが肝心のホワイトリストが何なのか定義していない」など、Webアプリ開発の初心者でも理解できて、すぐに実践できるような。

続・「正しい入力」「あり得る入力」「あり得ない入力」の話

いきなりタイトルをひっくり返すようだけれど、こう考えるほうが分かりやすい気がしてきた。

  • HTTPリクエストには、通常の操作で起こりえる「あり得るリクエスト」と、そうでない「あり得ないリクエスト」がある
  • 入力パラメータ(PHPだと例えば $_GET["hoge"])には、仕様上「正しい入力値」と、そうでない「正しくない入力値」がある

Strong Parametersの役割 は、「あり得ないリクエスト」を「あり得るリクエスト」に変換したり、例外としたりすることだ。だからこそ、モデルではなくコントローラに置かれた。

This new approach is an extraction of the slice pattern and we're calling the plugin for it strong_parameters (already available as a gem as well). The basic idea is to move mass-assignment protection out of the model and into the controller where it belongs.

The whole point of the controller is to control the flow between user and application, including authentication, authorization, and as part of that access control.

Riding Rails: Strong parameters: Dealing with mass assignment in the controller instead of the model

CSRF対策用のトークンが含まれない場合も「あり得ないリクエスト」。前回の記事でいう「あり得ない入力」がリクエストに含まれる場合も、「あり得ないリクエスト」として処理したければすればよい。

そうした処理を経れば、アプリケーションが処理すべきは「あり得るリクエスト」のみとなる。もちろん、「正しい入力値」と「正しくない入力値」の判別は済んでいないので、各入力パラメータの「バリデーション」が必要になる。

ユーザ入力のバリデーションは以上に尽きると思う。


ちなみに、以前の記事で触れた「ミクロ派」の主張(例:HTML構築時にも変数の文字エンコーディングをチェックすべき)は、ユーザ入力とは関係のない話だ。その変数はユーザ入力由来のものとは限らない。単純に、ぼくがテンプレートエンジンを作るとしたら、バインドされる変数の文字エンコーディングをチェックするだろう、といった程度の話だ。ライブラリやフレームワークが頑張ることで、アプリケーション開発者の負担や不安はむしろ軽減されるのではないか。

プロフィール
知識欲と謎解き欲が旺盛なWebエンジニア。AWS認定ソリューションアーキテクト&デベロッパー(いずれもアソシエイト)。JAPAN MENSA会員
カテゴリ別アーカイブ
記事検索