セッション管理は難しい
Web でユーザインターフェスを構築するにあたり、サーバサイドプログラムで面倒となるのがセッションの管理ではないだろうか。 よくある話なのですが、改めて書いてみる。
http の通信は1画面の送受信が終わればはいおしまいなので、サーバはクライアントを TCP/IP セッションを使って特定することができない。 つまり、同じクライアントに”次の画面”を出したくても、サーバはどれが同じクライアントの要求なのか分からないわけだ。
そこで、サーバが送信した情報の中に、クライアント(ブラウザ)を特定するような ID を送り込む手法が使われる。 このID はセッションID とかと呼ばれ、また、このようにクライアントを特定して動かすことをセッション管理という。
送り込む方法は3つで、 cookie 、post の hidden 、get の引数、のいずれかにセッションID を忍ばせる。 ログに残りやすいという理由で get の引数 ( hoge.cgi?sid=hogehoge )で実装するのはセキュリティ上あまりよろしくないとされる。(要は Apache のログを見るとすぐばれる、ということだと思われる)
この場合、hogehoge というセッションID 他に知られた場合、そのセッションID を使ってそのクライアントのフリをすることができる。 たとえば、銀行の残高照会をしているセッションID を知ることができた場合、そのセッションID を使って他人の口座情報をみることができてしまう。 この行為を、セッションハイジャックという。
cookie、post、get いずれの方法であっても、結局 http での通信である以上、セッションIDは平分で流れるわけで、多少の強度の違いがあれど脆弱性を持つこととなる。 完全にセッションハイジャックをはねたいならば、SSL などで暗号化するほかない。
と、言っているわけにはいかない場合もあって、たとえばハイジャックされたところで致命傷を負わない、がしかし、なるべくならされたくないシステム(たとえばブログの管理画面)などで、暗号化しないでセッション管理を実装することも多々あるわけだ。
で、ちょっと考えて PHPとデータベースで実装をしてみた。
- クライアントには cookie もしくは html(POST hidden属性) でセッションID を渡す
- セッションID には有効期限を持たせる(たとえば前回アクセスから10分以内であれば有効)
- 同一セッションであると認識するときに、セッションID のほかに、IPアドレス、USER_AGENT の同一性もみる
- 前回アクセス画面と、HTTP_REFFER が不一致していないかを検証する
これでだいぶ不正ははねることができると思うのだが、いろいろな条件が重なると正当なアクセスでも切れる可能性があって、悩みどころ。
たとえば HTTP_REFFER をださないブラウザだとセッションがつながらないだとか、NAT 環境であると、IPアドレスが全部同じになってしまって意味がないとか。
あと、結構はまるのが、負荷分散などでサーバが複数ある場合。 個別のアプリケーションサーバで独自に管理してしまうと、次のアクセスが違うサーバにいった場合につながらなくなってしまう。(分散装置と利用するアプリケーションサーバによっては、分散装置がセッション管理をして同じサーバにとばしてくれる場合もある)
うーん、なかなか難しい。
PHP はプロダクト自体にセッション管理をする機構が備わっているので、こちらも試してみようと思う。