<前の日記(2006-01-16) 次の日記(2006-01-26)> 最新

おおいわのこめんと (2006-01-25)


2006-01-25

[Security] hidden field に session ID を入れる CSRF 対策方法に対する反論に対する反論

ああ、禅問答のようなタイトル。

前から、「おさかなラボ」なるページで 高木浩光氏の推奨するCSRF対策は安全でない、との主張が 展開されていたので注目していたのだが、最近↑のページの再反論がでて、やっと僕の中では結論がでた感じ。

で、結論は、「やっぱり彼の主張は反論になってない」でよさそう。 正直、再反論の主張を見て、思ったような内容じゃなくてちょっとがっかり。

彼はずっと、「hidden フィールドはキャッシュから漏洩する」と主張していたので、 きちんと対策していてもそういうことが起こる状況想定を何か持っているのかと期待していたのだが、 再反論での主張は「たとえ Pragma: no-cache していても、cache のディスクに残る可能性はあるから駄目だ」 というだけだった……。

彼が見落としている、あるいは再反論の時点で意識から飛んでしまった点は2つあって、 1つめは、proxy のバグがある前提では、「Set-Cookie」ヘッダを含む あらゆるリクエスト・レスポンス情報が漏洩する危険性があるので、 たとえセッションIDを予測できない一時認証鍵を form に埋め込んだところで、 セッション鍵の漏洩を防げない、という点。 これは本質的で、特に彼の主張する「no-cache でもディスクに保存される」というセマンティクスを もつ cache においては、form の hidden の値はディスクに保存されるが、 session key をディスクに保存させないという状況は存在しないのである。 何故か彼は盲目的に Cookie は漏洩しないと思い込んでいるようだが、 Set-Cookie はレスポンスの一部である以上本文の一部である form のデータとは一組で扱われるし、 そもそも RFC2965 の Section 3.2.3 を見ればわかる通り、 Cache-Control: no-cache="Set-cookie2" か (彼が脆弱だと主張する) Cache-Control: no-cache を送出しない限り、Set-cookie はキャッシュに残り、 たとえ別のクライアントに対してであっても再送出されるのである。 *1

(余談) ちなみに、この Set-cookie をキャッシュする仕様、どんな使いみちがあるんか? と思うのは自然だと思うが、 RFC2965 には、「ログイン前の初期ページを要求した時点でセッションを expire させる Set-cookie を送出するケース」を 想定した記述がある。ま、普通は "Cache-Control: no-cache" と組み合わせて使え、ということですね。(余談終り)

ついでにいえば、Vary: Cookie ヘッダがついている (これは問題の form のあるページには付いているべき) レスポンスの返り値をキャッシュするためには、クライアントの送出した Cookie の値を一緒に保存しておかないと、 次回のリクエストの照合ができなかったりする。(そもそも no-cache の値をなぜ保存するんだ、という疑問は それが彼の置いた前提なのでおいておくとして。) さて、この状況でディスクから form の hidden 値だけが洩れて Cookie は洩れないシチュエーションって本当に考えられるのですか、というのが疑問。

(さらに余談) ちなみに、彼が言及している no-store 要求だが、これはサーバ側からの レスポンスの取り扱いの差異というよりは、リクエスト側で強い意味を持つヘッダのように思える。 リクエストの "Cache-Control: no-cache" は、基本的に「前の要求結果は欲しくないので、最新情報を取得してくれ」 と proxy にお願いするためのディレクティブである。一方、"Cache-Control: no-store" がリクエストに入っていると、 「この要求とその結果はディスクに保存しないでくれ」という要求を表している。さて、RFC2068 には "Cache-Control: no-cache" を持つリクエストに対する "Cache-Control: no-cache" の無いレスポンスを キャッシュしてはならないという記述はぱっと見て見当たらないような気がしてきたのだが、 見落としてるかな?……(さらに余談終り)

そして、「クロスドメイン脆弱性」を定義せずに使っている時点で どうか*2 と 思うのだが、クライアント側にバグと呼ぶに値する脆弱性があったら、 普通は form の値だけじゃなくて、cookie の値も漏洩していると思うのは、 自然かつ妥当だとおもうんけどね。 所詮は cookie は「自動送出される hidden パラメータ」なのであって、 form のパラメータ以上の安全性を仮定している時点で何かおかしい。 *3 で、当然ですが、セッション鍵が流出した場合、cookie 認証のみのページが1ページあれば、 あとは彼のページで列挙されているどんな対策を取っても破られます。どんな複雑な方法で一時鍵を製作しても、 問題のページから辿れば正規の一時鍵が取得できますから当たり前ですね。

やっぱりこの手のセキュリティの議論をする時に、 前提条件ははっきりさせないとマトモな結論は出ない、というのは だいぶ前にも書いたが、改めて確認しておきたい。 高木さんの前提は「XSS, Session Fixation, ブラウザのバグ, TCP/IP 盗聴」はないもの *4 としているのであって、その前提をひっくり返した反論を、その前提をひっくり返すだけの根拠を出さずに 述べても虚しさが残る。 もちろん、ブラウザ・キャッシュなどのバグに対してどれだけ頑強か、という主張を敢えて展開するのだ、というなら それは構わないが、その前提での議論としては現状の彼の主張は (特に自ら推奨する手法にも脆弱性が残るという点で)根拠が弱過ぎる。 そもそも、そんな物を前提にしたら大抵は「何やっても無駄」 *5 という結論になってしまう *6

ちなみに。高木対策案だが、hidden field の文字列がセッション鍵と同じ文字列である必然性ないセッション鍵が分からないと hidden field の値を推測・複製できない、ということだけがセキュリティ上の要件であり、 最も単純明解で即時実装が可能な方法で実装されたのがあの方式であると僕は理解している。 だから、別に「同じ文字列は気持ち悪い」という感情をどうしても抑えられなくて、 かつ SHA-1 とか簡単に使えるプログラミング環境を使っているのであれば、 HMAC 風味で適当な秘密の文字列と concat して SHA-1 とるとかして、 一方向性を持たせても一向に構わない*7。 セッション変数を使うのは問題に対して明らかに過剰で気に入らないが、 やりたければ完全乱数*8 にしてセッションIDに紐付けられたサーバ側セッション変数に保持しても要件は満たす。 ただ、所詮は cookie からのセッションIDの漏洩のリスクと同次元のリスクなので、 本質的にセキュリティレベルが上がっているわけではない。

追記 (2006-01-25)

やっとキャッシュ以外の脆弱性の話が語られたので 補足しておきますが、IE の標準設定以外に脆弱性があるのは当然の常識 (そもそも Active X を無条件に受け入れるとか、明らかな脆弱性があります) かと思います。 IE の非標準設定を許す前提では、何も(ローカルコンピュータのセキュリティすら)守れない、 という結論になります。これは IE というブラウザの大きな問題だと思いますし、 単独できちんと議論される問題だと思いますが、今回の話でいえば、 CSRF の特定の攻撃ができる設定だけが変えられて、Active X の項目とかは変わっていないという 前提は無理があります。

あと、当該ドメインに XMLHTTPrequest が送れ、レスポンスが受けとれる状態では、 別にその場で既存のセッションに乗っかって request を送り返せばいいだけですから、 どんなセッションキーを推測できない秘密の符丁を使っても正規のリクエストを送れます。 ですから、セッション鍵を盗む必要性が無くなり、他の対策も含めて CSRF 対策にならなくなります。 それも、スクリプト主導で入出力が両方できる(レスポンスに応じたリクエストを作れる) CSRF 発動状態というのは、ユーザが攻撃に気がついてセッションを閉じるまで *9、 本質的にセッション鍵が盗まれたのと同じだけの攻撃が可能ですから、 「セッション鍵が盗まれてないからより安全」とは言えなくなります。 「XSS とかがないのが前提です」というのも、要するに原理は同じです(微妙に惚けてたの修正)。 XSS がある状態では、たとえ tDiary のように session ID cookie がなくても受動攻撃を受けます。

*1 form のページと違うページでセットされることが多いのは事実だが、セキュリティ的に本質的な差異ではない。

*2 少なくとも FUD のうちの U は満たしてしまっている。

*3 未知の攻撃パターンを仮定した時には、あらゆるリクエストで自動送出される分だけより危険だ、とすら考えてもいい。

*4 この前提条件は高木氏自身が解説しているが、その妥当性は、これらは別途対策を要し、その対策をしないとどんな対策をしても CSRF と同等以上の脅威を原理的に防げないことが論証可能である、ということに依っている。

*5 proxy のバグに関しては、SSL を使って proxy には平文を1バイトたりとも触らせない、という結論はありか……。

*6 というか、それ以外の中途半端な結論は恐らく間違いである。

*7 正直、1月に1回くらいは、「SHA-1 くらいかけてもいいのになぁ」と思う一方で、 1月に30日くらい「あーでも本質的じゃないんだよなやっぱり」と思ってたりする。

*8 当然、この乱数は cryptographically secureか、 少なくとも セッションID以上に予測不可能な乱数 でなければならない。 この条件、/dev/random とかの存在を仮定して良ければ簡単だが、 スクリプト言語レベルで移植性を考えると結構厳しい。

*9 冷静に考えれば、これもセッション鍵盗聴の場合と同じなのですが。

[TrackBack URL: http://www.oiwa.jp/~yutaka/tdiary/trackback.rb/20060125 (note: TrackBacks are moderated: spams will not be shown.) ]

大岩 寛 (おおいわ ゆたか) <yutaka@oiwa.jp.nospam ... remove .nospam> .

Copyright © 2005-2014 Yutaka OIWA. All rights reserved.
Posted comments and trackbacks are copyrighted by their respective posters.

記事の内容について (Disclaimer / Terms and Conditions)