T.Teradaの日記

2006-11-21

[]ログイン直後のレスポンスの返し方 21:38 ログイン直後のレスポンスの返し方を含むブックマーク

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

とおりすがりとおりすがり 2006/12/02 16:21 302 Foundではなく303 See Otherが正解。
ログイン処理はSSL化すべきなので、Proxyで情報は抜けない。
SSL内でPOSTメソッドをリダイレクトすると、警告が出るかもしれない。
古いブラウザでは、POSTメソッドのリダイレクトで正しくページ表示しないかもしれない。
特定バージョンのブラウザの動作が、必ずしも正しいとは限らない、デファクトスタンダードはスタンダードではないのだから。
故に、SSL+ワンタイムトークンと言う選択に対する優位性はない。
そして何よりも、ログアウト処理するならその時点でブラウザを閉じさせれば済む話。
ログアウト後も、GETメソッドなコンテンツは見えちゃうんだから。

teraccteracc 2006/12/02 18:55 コメントありがとうございます。

>302 Foundではなく303 See Otherが正解。
余りその辺りは主題では無いので書いていませんが、HTTP1.1ならば303の方が良いですね。

>ログイン処理はSSL化すべきなので、Proxyで情報は抜けない。
SSLをMITMするProxyならば抜けます。Proxyは当然正規のサーバ証明書を持っていないので、ブラウザは警告を出しますが、リクエストを再生する攻撃者は警告を無視すればよいだけです。

>SSL内でPOSTメソッドをリダイレクトすると、警告が出るかもしれない。
>古いブラウザでは、POSTメソッドのリダイレクトで正しくページ表示しないかもしれない。
リダイレクト先が非SSLページならば、多くのブラウザは警告を出しますね。リダイレクト先がSSLの場合でも、警告が出るのか、正しくページ表示しないものがあるかは判りません。私個人の経験に過ぎませんが、一日数十万程度のログインがあるサイトで、POSTのリダイレクトをやっていましたが、その経験上不都合が生じたことは無かったです。

>特定バージョンのブラウザの動作が、必ずしも正しいとは限らない、デファクトスタンダードはスタンダードではないのだから。
それはその通りです。ここでは、何かの標準に則った話というよりは、現実的な話をしているつもりです。

>そして何よりも、ログアウト処理するならその時点でブラウザを閉じさせれば済む話。
それが一番確実なんだろうと思います。画面遷移設計上、それが許されるサービスなのであれば、確かにそうすべきだと思います。

>ログアウト後も、GETメソッドなコンテンツは見えちゃうんだから。
たぶんキャッシュの話だと思いますが(以下その想定で書きます)、キャッシュしないよう指示されたコンテンツは、通常ブラウザはキャッシュしませんよね。全てのブラウザがそれを守るわけでは無いでしょうけども、キャッシュが見える話と、PWが盗まれたり、なりすましでログインされる話は別だと思っています。

#かなり前提条件などを手抜きして書いた日記なので、指摘頂いた箇所のいくつかは本文に追記したいと思います(ブラウザを閉じる話などなど)。

金床金床 2006/12/02 20:42 予想通り303キタwww

>Proxyは当然正規のサーバ証明書を持っていないので、ブラウザは警告を出しますが
攻撃者がクライアントPCにルート証明書のインストールが可能な状態(高い権限を持っている状態)であれば、ダイアログが出ないようにプロキシをかますことができます。http://wizardbible.org/15/15.txt参照。

とおりすがりとおりすがり 2006/12/02 21:18 丁寧に1点づつご回答ありがとうございます。
SSLのリロード時に証明書が変わっても再POSTされるなら、大きなセキュリティホールと言えませんか?
試す価値あるかも。
「かもしれない」とした部分は、2000年頃はそんな動作をするブラウザがあったと言う事で、最近のものがどうなってるかは知りません。
しかし、2000年頃から家庭にノートPCが急速に普及しはじめていますので、98SEやMEは健在だったりします。
Macに至っては、進化が停止していた時代がありますので、そうしたブラウザでも動いてくれるかが気になるところです。
そう言えば、ケータイも大丈夫なんだろうか?
ユーザー切捨てに繋がらなければ支持したいと思います。
(実は、そこが気になってるだけだったり)
デファクトスタンダードの怖さは、例えば問い合わせフォームを問い合わせ後に確実に閉じさせるためにポップアップにしていたら、ポップアップブロックで作り直しになったなど、仕様変更で泣く事があります。
(これで泣いた人が身近にいたり)
なので、たまたまそう動いている話なのか、そう動く事が正しいのかが気になると言う話です。

b-windb-wind 2006/12/03 02:42 今すぐに正確な情報が出ないですが、Softbank の古い機種で、
> POSTリクエストへのレスポンスに302応答を返した場合リダイレクト応答をPOSTで行なう
物がいくつかあり、実際にその機種を使っている方もかなりの数いらっしゃいます。
これは公式ドキュメントにも仕様として書かれていました。
携帯サイト運営上での経験です。

teraccteracc 2006/12/03 13:40 皆様色々とご意見・情報ありがとうございます。

とおりすがりさん:
>SSLのリロード時に証明書が変わっても再POSTされるなら、大きなセキュリティホールと言えませんか?

手元のWinXP SP2のIE6.0と同OSのFirefox2.0では、証明書が変わっても再POSTできます。その際にSSL証明書の警告は出ますが(ルート証明書をブラウザに食わせてないので)、ダイアログで「続行」すればPOSTデータを再送します。証明書が変わった場合は、どうやっても再送できない方が良さそうですが、セキュリティホールとまでいえるかというと微妙だと思います。

>ユーザー切捨てに繋がらなければ支持したいと思います。

以前の職場のシステムでは、POST+302してました。OSはWin98〜/MacOS9〜、ブラウザはIE4〜/NN4〜でテストしていましたが(全バージョンは網羅してないです)、その限りでは問題なしでした。その経験で言えば、現実的に切り捨てられるユーザは少ないと思います(ゼロではないでしょうけども)。

>なので、たまたまそう動いている話なのか、そう動く事が正しいのかが気になると言う話です。

HTTP1.1では303を返す前提で、HTTP1.0+POST+302での考えられる問題点を書くと、
(1)動かない or ダイアログを出す環境があるかも
(2)POSTで自動リダイレクトする環境があるかも
(3)リロード再生できる環境があるかも
の3つかと思います。

1は、規格(RFC1945)に忠実であれば、ダイアログを出して、OKならPOSTでリダイレクトするんでしょう。「動かない」についても、bodyにリンクを書いておけば、何とかなるんじゃないかと。

2については、POST再送の際に、ID/PWをリダイレクト先に送ってしまうので、B対策はセキュリティ的にはむしろマイナスですね。リロードでの再送の件も多分解決しないです。規格に忠実なクライアント環境を前提とするシステムでは、302は得策じゃないですね。

3は、そのような環境ではB対策の効果は無いですね。HTTP1.0/1.1ともRFCには記載が無さそうです。どなたか知っていたら教えて下さいませ。

b-windさん:
>今すぐに正確な情報が出ないですが、Softbank の古い機種で、(略)
携帯は考えていなかったのですが(日記には、全然前提条件が書いてないですね、すみません)、私が書いた図は携帯画面に見えますねw。それはともかく、参考になります。ありがとうございます。

teraccteracc 2006/12/03 14:11 金床さん、コメントありがとうございます。

>予想通り303キタwww
以前せっかく指摘頂いたのに、全然きちんと書けてませんでした。ぅぅ。

>攻撃者がクライアントPCにルート証明書のインストールが可能な状態(略)であれば、ダイアログが出ないようにプロキシをかますことができます。

私が書いた日記の攻撃では、攻撃者が警告ダイアログを受け取るので、余りダイアログを消す必要は無いです。また高い権限が無くてもできる攻撃だと思っています。

wizardbibleの記事を読ませてもらいました。
>このプロキシサーバーは、クライアントが接続を希望しているホスト名を取得したのちに、そのホスト名をCNの値として持つX509証明書をリアルタイムで作成し

なるほど。知らないホストと接続する度に、これが必要ですが、Javaだとできるんですね(opensslを叩けば、他の言語でもできるかもしれませんが)。貴重な情報&jarファイルありがとうございます。

>getBogusSslSocket
以前、インチキ証明書を入れたテスト環境で、他システムと対向試験をしたときに、これのやり方が判らず苦労したことを思い出しました。あんまり本題と関係ない話ですw

金床金床 2006/12/03 16:52 >私が書いた日記の攻撃では、攻撃者が警告ダイアログを受け取るので、余りダイアログを消す必要は無いです。また高い権限が無くてもできる攻撃だと思っています。

はっ、その通りですね。攻撃フローを良く考えてから書くべきでした。失礼しました。

>以前、インチキ証明書を入れたテスト環境で、他システムと対向試験をしたときに、これのやり方が判らず苦労したことを思い出しました。あんまり本題と関係ない話ですw

あるあるw