21日の日記「Session Fixation脆弱性の責任主体はWebアプリかWebブラウザか」で、ブラウザベンダはCookie Monster問題をどうするのだろうかということについて書いたが、Firefox 2.0 について調べてみたところ、解決していなかった。また、IE 7 も解決していない。
そのような状況では、セッション追跡がcookieだけによって行われている場合であっても、Session Fixation攻撃に対して配慮せざるを得ない。
これまで、Session Fixation対策といえば、ログイン後の状態をセッションハイジャックされることの防止のみが語られることが多かったが、ログイン前についてはどうだろうか。
たとえば、ログイン前から使えるショッピングカートに対してSession Fixation攻撃が行われると、攻撃者の罠から訪れたと気づかずにカートに商品を入れる被害者は、何をカートに入れているかを攻撃者に見られてしまうことになる。これについては、そのくらいはまあ許容範囲ではないか、という考え方もあるだろう。
しかし、たとえば、ログインなしで決済処理をするネットショップで個人情報やカード番号を入力する画面や、ユーザ登録画面などで、個人情報を入力していくときに、途中の画面をセッションハイジャックで覗かれたときに、秘密にすべき情報が攻撃者に見られてしまうというのは、許容範囲ではないということになるかもしれない。
昔は、セッションIDを用いたセッション管理は、ログイン機能の実現のために用いられることがほとんどだったと思うが、最近は、Webアプリケーションサーバ製品(Webアプリミドルウェア、Webアプリフレームワーク)が普及したことで、開発者が自前で設計することなく手軽にセッション管理機構を利用できるようになったため、ログイン機能のない場面でもセッション管理機構が活用されるようになったようだ。
つまり、昔は、情報入力画面が複数ページにまたがるときは、1ページ目で入力した値を、2ページ目ではフォームの hiddenなINPUT要素に入れておいて、次ページへ引き継ぐように実装していたものだったが、最近では、セッション管理機構を活用して、1ページ目で入力した値をWebアプリサーバのセッションオブジェクト中に記憶させ、hiddenなINPUT要素を使わない実装が出てきているようだ。
このような実装の場合、ログイン前Session Fixationによって、入力中のデータを攻撃者に閲覧されてしまう場合が生ずる。
対策としてまず考えられるのは、送信したデータはそれ以降、画面に(HTTPレスポンスに)出さないということだが、そうすると、入力したデータの確認画面を実現できないので、これは現実的でない。また、攻撃者は、閲覧だけでなく、データの変更操作も可能なので、被害者がカード番号を入力した後で決済を実行する前のタイミングで、商品送付先を変更して先に決済を実行という攻撃の被害も考えられる。
Session Fixationでログイン後の状態を乗っ取られないための対策として一般的に言われるのは、ログイン成功後に新しいセッションに切り替える(旧セッションと切り離す)ことだが、ログイン前Session Fixationの防止では、その「ログイン成功」というタイミングが存在しない。
同じ考え方をログイン前Session Fixationに適用しようとすると、ページ毎に毎回セッションIDを変更するということになるだろうか。あるいは最初のページだけでもよいか? 最初のページで情報を入力して2ページ目に移行すると、セッションIDが切り替わり、古い方のセッションIDは無効とする。これにより攻撃者はその2ページ目を閲覧できない。しかし、攻撃者がSession Fixationで被害者を2ページ目に誘導した場合はどうなるかというと、被害者がそれを不自然な状態だと気づかずに情報入力(途中からではあるが)を開始してしまえば、その情報が攻撃者に閲覧される(ような画面構成の場合がある)ので、やはりページ毎にセッションIDを変更しないと駄目だ*1。
しかし、そのような方法で対策するよりも、複数ページに渡る情報入力画面については、セッションを利用せず、hiddenなINPUT要素でデータを引き継ぐ方法を使うべきではないだろうか*2*3。
*1 このとき、セッションオブジェクトを shallow copyで引き継ぐ場合は注意が必要で、コピー前から存在したオブジェクトに新たに入力されたデータを格納するときは、それより前に旧セッションを無効化しないといけない。
*2 hiddenと言っただけで、いわゆる「価格改ざん」攻撃のことを言い出す人がいるかもしれないが、ユーザが入力した情報のみを hiddenなINPUTに引き継ぐようにするのだから、その問題の影響は受けない。ページ毎に行っている入力のvalidationがすり抜けられるという点については、最後でvalidationすればよい。各ページでのvalidationはUIとしての機能(クライアントサイドスクリプトで処理してサーバでは行わないのでもよい)であり、最後でのvalidationはWebアプリとしての機能と捉えればよい。
*3 type="password" なINPUTに入力されたデータを type="hidden" に入れて引き継ぐことが問題であるとするなら、パスワードは最後の画面で決めさせる設計にする。(もっとも、最初にパスワードを決めさせる設計には別の問題があるかもしれないが。)