読者です 読者をやめる 読者になる 読者になる

私が間違っておりました

PHP

CodeIgniterが急激によくなってきた - なんたらノート 第二期

で書いた、

バリデーションがダサい

まずライブラリのメソッドの中身に「$_POST」がハードコーディングされているため、任意の変数をバリデートできない。

バリデーションは「何らかの境界で、正体不明な外部のデータを、内部で使える形式(型)に変換する試み」のこと。けっこう間違っている人が多いけど、主題は「変換」であって、「検査」ではない。検査はベリフィケーションと言う。さて、Webで正体不明なデータとは何か、そいつはポストデータのことだ。自分で作った値や、すでに保持しているデータは正体がわかっているので、それらを比較照合しても、(Webの入出力境界においては)バリデーションとは呼ばない。単に、データ不整合のエラーと解釈するべき。

たしかに、ポストデータのバリデータに含まれるチェックルーチンを再利用したい時はあるが、Webフレームワークのバリデータが持つ責務は、外部からの変数を安全に持ち込むことだ。それは、自分が必要としている検査と同じだろうか?自分のアプリケーションのデータには自分で責任を持つのが、適切な責務分担だと思う。

という部分、厳密には私が間違っておりました。いかんいかん。こんなこと書いてたらセキュリティのおじさんに怒られる。

ユーザがフォームに妥当なデータを入力するのを助ける、という側面だけ、つまり、システムと人間の間での入力部分だけ切り出すと、たしかに、ポストデータだけでいいと思えるんだけど、「正体不明な外部のデータ」はそれだけじゃない。HTTPリクエストに含まれてやって来る文字列はすべて、自分の管理下にない系で作られた「外部の変数」と考えるべきだ。極端な話、User-AgentやAccept-Encodingヘッダ項目、はたまた、ヘッダ項目名そのものすら、改ざんされているかもしれない。

「人間と機械の関係以上に、インターネットのクライアントとサーバの関係は信用できない」これ大事。

ポストデータ以外も、バリッドであるかどうかは、毎回ちゃんと確認すべきだった。クェリパラメータ、パス要素中の任意部分、クッキーについては、いったんシステムが受け入れて使える形にしたもののはずだが、誰がそれを手動で書き換えて送信して来るかわからない。

実例。たとえば、クッキーはユーザが編集しないと思い込んでアプリケーション設計すると、クッキーにSQL文の一部に使う文字列を保存してしまい、SQLインジェクションされる。

というわけで、それを根拠に「ポストだけでいいじゃん」と言ったのは間違いでした、ごめんなさい。まぁ、じっさいバリデータが役に立つのはポストだけなんだけど、それでいいと思う動機がまずかったです。

CIバリデータの評価が甘すぎたので、もういちどちゃんと考えよう。

  • CIのバリデータは、「ページのフォームからユーザが入力した値」だけを対象にしている
  • 人間が打つ文字以外のデータについてはCIバリデータの対象外
  • ユーザとの対話のためのビュー技術であって、システム境界のアダプタではない
  • セキュリティを論じる次元とは違うもの
  • UIの対話ヘルパなので、フィードバックを返すフォームページがあることが大前提
  • フォームをmethod="GET"で受け取ることは想定していない
  • クェリパラメータつきのリンクは入力フォームがないので対象外
  • そもそもCI的には、クェリパラメータは存在しないものらしい(w

Webアプリケーションのフレームワークの多くは、結局バリデータのフィードバックをフォームページに返すので、明示的に変数を入力するバリデータにしても、ポストデータ(GETによるフォーム送信は無視)を間接的に使うバリデータにしても、やることは同じなんじゃないかと思う。だとすれば、ユーザコード中の変数に一度も外部データを取り込むことなく、インバリッドなケースを特定してフォームに制御を返せるほうが、なにかと安全。とくに、代入しただけで副作用処理が走る変数を作れる言語では、ユーザコード中の変数に外部変数が一度も入らないのはありがたい。PHPにそういう言語機能はないけど、むりやり例を挙げると、

$my_formdata->hoge = $_POST['hoge'];

の代わりに、

$my_formdata->set('hoge', $_POST['hoge']);

としてから$my_formdataをバリデータにかけようって感じ。これだと、インバリッドなデータのせいで、setメソッドの副作用が発動するかもしれないよね。そんな不注意はないだろう、と評価するまでもなく、データが入ってこなければ不注意が発生しようがない。この「注意力ゼロで済む」ところが「なにかと安全」の意味ね。

それじゃ、CIのセキュリティはというと、

セッション(Cookie保存のあれ)

  • 自分が生成したものかどうかを照合するらしい
  • 改ざんされた可能性が極めて低い、という確認は自動的になされている
  • なので、すでにバリデーション済みのデータをもとにしているかぎり、個々に再バリデーションする必要はない


これが本当なら、PHPの$_SESSIONには戻れない

SQLインジェクション

  • SQLクェリはできるだけフレームワーク提供のビルダで作るべし
  • 文字列結合については、プログラマが自力で注意すべし
  • データベースにおけるシステム境界はWebサーバのシステム境界とは違う
  • データベースにSQLを送る直前で意識するべき


とっても普通、型制約の強い言語のO/Rマッパーでないかぎり、このへんで限界

XSS等のHTML改ざん攻撃

  • なにやら怪しい文字列をサニタイズするフィルタがあった
  • 多くの場合、サニタイザを生かしておけばよさそう
  • プログラムコードを入力させたい場合はどうするのか?その項目だけ意識して、独自のサニタイザでビューに吐くのが正しいのかな


SQLサニタイズと称して文字化けを起こすPHPの某機能よりはマシ。壊されるのはあくまで、やばそうなコードっぽいものだけ。

クェリパラメータ(GET)

  • CI曰く、「そんなものは受け付けません、絶対禁止」がベストプラクティスだそうな
  • 勝手に受け付けた場合、フレームワークの設計範囲外なので独自に対処する必要アリ
  • クェリパラメータ検査ヘルパをCIがやらないなら、プラグインとして作るべきかな


CodeIgniterの余計なお世話 - なんたらノート 第二期 見てね

クェリパラメータについて以外は、おおむね妥当な品質だと思う。

開発者のスキルに依存して、セキュリティ対策という車輪をいつも再発明するのに比べ、とりあえずレギュラータイヤは付いてる感じ。誰もが必要なものがあり、誰にとってもありすぎじゃない、ちょうどいいバランスだと感じる。一部「CodeIgniterの余計なお世話 - なんたらノート 第二期 」を除いて(w