ログイン直後のレスポンスの返し方

多くの会員制Webサイトでは、ID/PWによるログイン処理がある。ユーザにログイン画面を提示し、ユーザがフォームにID/PWを入力してsubmitする。ID/PWがOKであれば、ユーザのブラウザにはログイン後の画面が表示される。

以下に、これを実現するための2通りのシーケンス図を描く。セキュリティの観点で望ましいのはA、Bのどちらだろう?というのが今回のテーマ。

Aではログイン要求に対してHTTPステータス200応答とともにログイン後画面をブラウザに返している。Bではログイン要求に302応答を返して(HTTP1.1では303応答)、ログイン後画面にリダイレクトしている。

結論を言うと、セキュリティの観点では、私はBの方が望ましいと考えている。

逆に言うと、Aにはどうにも気に入らないところがある。それは、ID/PWを含むリクエストに対して200応答を返していることだ。200応答のページは、ブラウザの戻る/更新ボタンによりリロードすることで、リクエストパラメータをサーバに再送信させることができるのだ。

以下のような状況(PC端末を共有している状況)を想定する。

  1. ユーザX(被害者)が、あるサイトにログインし、いくつかのページを見て回り、その後ログアウトする。そして、ブラウザを閉じないままにPC端末を離れる。
  2. 次に、ユーザY(攻撃者)が、ユーザXが使っていたPC端末を使う。

ユーザYは、ブラウザの戻るボタンで、ユーザXのログイン直後の画面に戻って*1、更新ボタンを押すことで、POSTデータ(ID/PW)をサーバに再送信させることができてしまう*2。つまり、ユーザXのID/PWはサーバに再送信され、ユーザYはユーザXとしてサイトに再ログインできてしまうことになる。

ログイン画面にワンタイムトークン的なものを埋め込んでおけば、ユーザYに再ログインされる事態は避けられるが、別の方法の攻撃が可能だ。ユーザYはプロキシ(SSL対応のプロキシ)を用意し、このプロキシを使うように、ブラウザの設定を変更することができる(プロキシ変更にはPC端末の管理者権限は不要)。ユーザYは先に述べた方法で、POSTパラメータをブラウザに再生させ、プロキシでID/PWを奪うことができてしまう*3

一方、302/303応答では、リロードによるリクエストの再生はできない(少なくともIE6.0 SP2で私が試した限り)ため、PC端末を共有するような状況でも、上記のような問題は発生しない。従って、この種の攻撃を想定する場合、AとBをセキュリティの観点で比較すると、Bの方が望ましいと考える。

なお、「リロードによるリクエスト再生」の問題は、ログイン画面だけのものではない。だが、一般にログインは、他と比べて使用頻度が高い機能なので、ログインでは可能な限り「リダイレクト応答を返す」のがよいだろう。

AとBの比較という観点を離れるが、この種の問題を避けるために最も効果的なのは、ログアウトした時点でウィンドウを閉じることだ。そうすれば、少なくともきちんとログアウトした(もしくはウィンドウを閉じた)ユーザに関しては、上記の問題が発生することはない。この種のリスクに対処する必要がある場合、画面遷移設計上の制約が無ければ、ウィンドウを閉じる対策をするのが望ましい。*4

また、リダイレクトを採用する場合には、いくつか注意点がある。まず、多くの開発者の方が経験されていると思うが、非SSLの画面にリダイレクトすると、多くのブラウザが警告を発すること(ブラウザの設定にも依存する)。さらに、HTTP1.0の規格に忠実なブラウザでは、POSTリクエストへのレスポンスに302応答を返した場合、ダイアログを表示したり、リダイレクト応答をPOSTで行なったりする。私の過去の経験上、メジャーブラウザ(IE5、NN6以上)では、そのような現象は確認できなかったが、もしもリダイレクトを採用する場合、対象とするシステムが稼動保証するクライアント環境でのテストが必要となる(私が言うのも、おせっかいな話だが。。。)。

最後に、言うまでも無いことだが、いくつか追記したい。

  1. 端末のシェアを考慮すべきクリティカルなシステム以外では、この日記に書いたようなリスクは無視できる
  2. 端末シェアを考慮する必要があるならば、この問題は山ほどある問題の一部
  3. この問題(POSTの再送の問題)は新しい問題ではなく、昔から存在し指摘されていたもの

参考:同じことを書いているサイトを発見
Stealing passwords via browsers
他の方のはてなの日記にもあったが、発見できずにいる。

#金床さん、先日はコメントありがとうございました^^

*1:単純に戻るボタンで戻っても、ローカルキャッシュも認証チケットも無いため、ユーザYがユーザXのログイン後の画面を表示させることはできない。

*2:リクエストがPOSTであったページをリロードすると、IEでは「情報を再送信しないと、ページを更新できません。。。【再試行】【キャンセル】」というようなダイアログが表示される。ここで再試行ボタンを押下すると、POSTパラメータはサーバに再送される。

*3:SSLの場合、プロキシがサーバになりすますことになるが、このプロキシは正規のサーバ証明書を持っていないため、非正規のサーバ証明書を使うことになる。このため、ブラウザが警告ダイアログを表示するが、攻撃者は警告ダイアログで、続行しますか?→「はい」を選択すればよい。

*4:オンラインバンキングでは、ログインの際にウィンドウを立ち上げ、ログアウトの際にはそれを閉じるサイトが多いようだ。また、私が使用する銀行サイトでは、ログイン時にリダイレクトをしている(そうでない銀行サイトも使用している)。ただし、上記のような問題を避ける目的で、銀行サイトがそのようなことをしているのかは判らない。