私が間違っておりました
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クェリはできるだけフレームワーク提供のビルダで作るべし
- 文字列結合については、プログラマが自力で注意すべし
- データベースにおけるシステム境界はWebサーバのシステム境界とは違う
- データベースにSQLを送る直前で意識するべき
↓
とっても普通、型制約の強い言語のO/Rマッパーでないかぎり、このへんで限界
XSS等のHTML改ざん攻撃
- なにやら怪しい文字列をサニタイズするフィルタがあった
- 多くの場合、サニタイザを生かしておけばよさそう
- プログラムコードを入力させたい場合はどうするのか?その項目だけ意識して、独自のサニタイザでビューに吐くのが正しいのかな
↓
SQLサニタイズと称して文字化けを起こすPHPの某機能よりはマシ。壊されるのはあくまで、やばそうなコードっぽいものだけ。
クェリパラメータ(GET)
- CI曰く、「そんなものは受け付けません、絶対禁止」がベストプラクティスだそうな
- 勝手に受け付けた場合、フレームワークの設計範囲外なので独自に対処する必要アリ
- クェリパラメータ検査ヘルパをCIがやらないなら、プラグインとして作るべきかな
↓
CodeIgniterの余計なお世話 - なんたらノート 第二期 見てね
クェリパラメータについて以外は、おおむね妥当な品質だと思う。
開発者のスキルに依存して、セキュリティ対策という車輪をいつも再発明するのに比べ、とりあえずレギュラータイヤは付いてる感じ。誰もが必要なものがあり、誰にとってもありすぎじゃない、ちょうどいいバランスだと感じる。一部「CodeIgniterの余計なお世話 - なんたらノート 第二期 」を除いて(w