●リファラーで発信元をチェック
HTTPリクエストを受けたとき、そのリクエストがどこのWebページから発行されたものかを示すリファラー(REFERER)と呼ばれる情報を得ることができる。この情報を活用し、本来意図したWebページ以外からのリクエストを拒否することで、CSRFによる外部からのリクエストを防ぐことができる。
ただし、ユーザーがリファラー情報を出力しないブラウザを使っている場合、このチェックを導入すると正当な操作でも受け付けなくなってしまう。
懸念されるリファラー情報偽装に対する問題だが、以前はリファラー情報を発行するのは攻撃を踏んでしまった自分自身なので、リファラー情報を偽装する動機がなく、この対策は安全であるとされていた。しかし、Flashのアクションスクリプトを利用することでではRefererヘッダーを自由に作成できてしまうため偽装できてしまう。よって、この対策だけでは安全と言えなくなった。
修正履歴
【2006/11/8】 リファラーで発信元をチェックについて、Flashのアクションスクリプトを用いることでリファラーを偽装できる手段が発見されましたため、内容を全面的に見直しました。
またそれか。Refererのクロスドメイン送信がデファクトスタンダードになったとでも言うのかね? そんなのは Flashの仕様の脆弱性*1とするべきものだ*2。独自の対策において配慮に入れるのは自由だが、あるいは、回避策として伝えるならわかるが、あたかもそれがスタンダードになったかのように公衆に伝えるのはやめてもらいたい。*3
Webアプリケーションはサーバ側だけのシステムではない。法律ではわざわざ「電子情報処理組織」という用語を用意しているくらいで、クライアント側と一体になってひとつのシステムを構成するものである。しかも、クライアントはそのサーバ専用ではなく任意のサーバ向けであるし、サーバもまた複数の種類のクライアントによって利用される。したがって、あるセキュリティ上の脆弱性が存在するとき、その原因がサーバ側にあるのかクライアント側にあるのか、その責任の切り分けが常に重要となる。
しかしながら、Webアプリケーションで用いられる各技術要素は、一部を除いて、セキュリティに関わる仕様が標準規格として明確にされているわけではない。HTTPの規格は存在しても、その上で動く「Webアプリケーション」についての規格というものが存在するわけではない。結果論としての攻撃可能性の有無はクライアント側のブラウザ等の個々の仕様に左右されるが、全体の標準が何かひとつのブラウザ仕様によって決定されるわけではない。
したがって、ブラウザ側の脆弱性は実装の欠陥だけを指すものではなく、仕様の欠陥を指すこともある。ある製品の仕様が欠陥であるかどうかは誰が決めているかというと、それは、業界のコンセンサスによって暗黙的に決まっていると言うほかないだろう。
そうしたコンセンサスを決定付ける大きな要素は、BUGTRAQなどの脆弱性報告の場であろう。特に脆弱性を発見して公表する人の役割は大きい。発見者は、発見した現象の原因がどこにあるかを示し、それがどのように危険であるかを示しながら、製品側の仕様に問題があるという帰結を導いて公表する。これに反対意見がなく、製品ベンダーがそれを修正するならば、あるいは、追試や類似事例の報告の積み重ねによって、その考え方が強まったならば、そこで新たなデファクトスタンダード*4が形成されたということになる。
Refererの話に戻ると、攻撃者が任意に指定したRefererを被害者に送信させることはできないということが、デファクトスタンダードになっているかどうかは、tDiaryのCSRF脆弱性の解決策を導き出す際に、職場の同僚の大岩さんと議論した(2005年7月の話)。その議論の流れは概ね次のようなものだったと記憶している。
高木: CSRFの解決策なんだけど、Basic認証の場合をどうするかなのよね。tDiaryとか。
大岩: tDiaryはたしかにCSRFできちゃうでしょうね。
大岩くんは、tDiaryでCSRFが実際に可能なことを実験してみせた。
高木: やはりね。対策なんだけど、既存の秘密情報がパスワードしかない。ユーザ名はCGIの「HTTP_USER」で取れるんだけど、パスワードって取れないの?
大岩: できない。できないようになっている。
高木: どうして?
大岩くんは、CGIでBasic認証のパスワードを取得できないようになっている理由を説明した。
高木: なるほど。それはそれで興味深いけど別問題として置いといて、そうすると次は、秘密情報を乱数で作る方法なんだだけど、Rubyに secure random(暗号学的に安全な擬似乱数生成系)ってある?
大岩: なかったと思う。ないですね。
高木: /dev/urandom を直接使うのはどうなの?
大岩: OSによっては存在しない。tDiaryは実行環境としてOSを指定していないので、どのOSでも使えるようにしないと。
高木: ここで自作するのはどうかと思うね。Rubyが用意するべきだから。そうすると次は、時刻情報を使う方法だけど、HMACとかの鍵付きハッシュを使うことになるので、鍵の管理が生ずるよね。
大岩: うん。いやな感じですね。
高木: そう。tDiaryは個人で使うとは限らないから、ユーザの安全のための鍵管理を、管理者が責任を負うというのは避けたいね。ときどき変更するべきとか、十分に予測できない文字列にすべきとか。ユーザのパスワードを使えば、ログインのセキュリティと同等にユーザの責任とできるのだけど、パスワードは取得できないわけで。そうすると、ユーザごとの固定のキーをユーザに設定させる方法になるのかな?
大岩: 秘密情報として弱いのでは?
高木: そうだね。ただ、弱いキーが設定された場合はユーザの責任にできる。そうすると、Refererチェックの方がよい場合もあることになるんだけど……
大岩: で、例の話ですね。
高木: Referer送出を止めている人の件ね。昔のNorton Internet SecurityではデフォルトでRefererを止めていたらしいけど、最近のバージョンではデフォルトでは止めてないらしい。今時だと、止めている人はMozillaのextensionとかでサイトごとに個別に止めてるんじゃないか。だから、ユーザの選択で、固定のキー方式か Refererチェック方式か、選ばせるという方法が考えられる。
大岩: Referer方式は、同じホスト上のページに罠があるとチェックになりませんね。tDiaryは日記ですから。でも、これは POSTに限定することで防げますね。
高木: POSTでのリンクを書き込まれたら?
大岩: コメント欄やトラックバックにはHTMLタグを許していないので POSTリンクは作れない。日記本文には POSTリンクを作れるけど、tDiaryでは、異なるユーザの日記は異なるホストに配置することになっているので、Refererでチェックできる。
高木: 異なるホストとは?
大岩くんは、同一ホストに異なるユーザの日記本文が書ける場合の脅威について説明した。
高木: なるほど。
大岩: ところで、Refererって本当に偽装させることができないことになってるんですか?
高木: たとえば?
大岩: リダイレクトとか。
大岩くんはリダイレクトについて調査した。大岩: リダイレクトでは起きないようになっているようですね。Ajaxはどうなってるんですか? XMLHTTPはヘッダがセットできるんじゃなかったでした?
高木: ヘッダがセットできることは新しくない。XMLHTTPとかはJavaアプレットと同類で、Javaアプレットでは90年代のころから、URLConnection に setRequestProperty というメソッドがあってヘッダをセットできるようになっていたんだけど、アプレットの置かれているホスト以外には接続できないよう、セキュリティ制限がかかっているので、問題は起きないようになっている。XMLHTTPも当然、同様の制限がかかっているはずだよね。
大岩: 他のサイトを表示させる方法ってありませんでした?
高木: AppletContextクラスの showDocument() メソッドのことかな? これは、ブラウザを指定されたURLにジャンプさせるだけの機能なので問題ない。アプレットで結果を受信できないからね。ヘッダをセットする方法も用意されてないし。
大岩: ふーん。
高木: というわけで、tDiaryの対策なんだけど、Refererチェックをデフォルトとして、Refererを止めてる人向けに、固定キーをユーザの責任で設定させてそれを使う方法も選べるようにするというのがよいと思う。
大岩: 了解、実装します。デフォルトは Refererの方にして、設定で変更するときだけ Refererオフを解除してもらうようにします。
高木: 設定画面のUIに工夫が必要だね。うまく説明しないと。
二人は、修正案をtDiaryの開発元に伝えるとともに、tDiaryのCSRF脆弱性としてIPAに届け出た。
数日後……
大岩: IEは信頼済みサイトゾーンだと、XMLHTTPは任意のサイトへ接続できるという話が話題になってますね。
高木: ほほう。セキュリティ設定の「ドメイン間でのデータソースのアクセス」が、信頼済みサイトゾーンでは「有効にする」がデフォルトになってるからかな。まあ、信頼済みサイトゾーンに登録する人の責任かな。
大岩くんは、実験してみた。
大岩: たしかにその設定だと任意サイトへ接続できるようですが、Refererはセットできないようですね。他にもセットできないヘッダがあるようです。
高木: ほほう。そういえば、TRACEメソッドも使えなくなったという話をどっかで見たよ。Microsoftはどういう理由で止めるようにしたんだろうね。
大岩: 例の、CGIでパスワードが取れないようになっている理由と同じじゃないですか? Mozillaはどうなってるんだろ。
大岩くんは、実験してみた。
大岩: ヘッダは禁止されてないようです。でも、Mozillaに信頼済みサイトゾーンはないし、任意サイトへの接続は禁止ですからね。
翌日……大岩: XMLHTTPの脆弱性を発見しましたよ。Mozillaで、リクエストヘッダのフィールド値に改行コードが入れられちゃいます。
高木: おお。HTTPレスポンス分割のクライアント側版ということ?
大岩: そう。いわば「HTTP Request Splitting」ですね。それから、(略)
大岩くんは見つけた脆弱性を説明し、脆弱性レポートをMozillaに報告した。さらにいくつかの発見をして、追加レポートをMozillaに報告した。
高木: その脆弱性を使うと、ドメインをまたがってリクエストを送れるので、Refererを偽装させることができるけど、ブラウザの脆弱性だね。
大岩: そう。
高木: request splittingしないで単に Referer:をセットする件はどうなった?
大岩: 脆弱性レポートで最後に「the following headers should not be overwritable」として Refererと Dateと Upgradeと Viaも挙げたんだけど、同一ホスト制限があるから問題が見つからないと言われたので、まあ blacklist じゃなくて graylist だと言った。修正されないかも。
高木: まあ、セキュリティの問題はないとしても、HTTPの仕様として、アプレットやXMLHTTPからのリクエストというものにおける Referer というのをどう考えるかだね。RFC 2616によると、「The Referer field MUST NOT be sent if the Request-URI was obtained from a source that does not have its own URI, such as input from the user keyboard.」とあるけど、これはどういう意味かな――(1)。まあ、XMLHTTPで取得したページのURIを Refererとして次の XMLHTTPのリクエストを出すというのは間違っていないかも。そうすると、Refererのセットを全部禁止するというわけにもいかないのかなあ。
大岩: ところで、XMLHTTP以外にも同類のものに脆弱性があるかも? Flashとかスクリプトがあるんでしたよね?
高木: あるかもね。Flashは作ったことがないので知らないけど、JavaScriptのようなものがあるらしい。PDFとかもあり得るのかな。既に誰かが調べてるだろうね。
詳細は大岩さんが技術文書の形式でそのうち書いてくれるだろう。
このように、前例に照らして、同一ホスト以外に任意のヘッダを送ることは、それを許すような製品があればそれは脆弱性であるというコンセンサスが業界にあると判断した。
そしてその後、2006年7月に、Amit Klein氏(Smuggling系の脆弱性発見で知られる)が「Forging HTTP request headers with Flash」という記事をBUGTRAQで公表した。
この問題は私なりに整理すると次のように言える。
まず、FlashのActionScriptには、任意の指定のURLへのアクセスを発生させるものとして、getURL() というAPIが古くからあったらしい。これは、Javaアプレットにおける showDocument() と同じで、ブラウザを指定のURLにジャンプさせるものらしい。(ただし、JavaのshowDocument()では POSTメソッドによるアクセスを生じさせることはできなかったのに対し、ActionScriptのgetURL()では、POSTメソッドも可能らしい。)
それが後にFlash Player 5のバージョンでXMLクラスが、Flash Player 6でLoadVarsクラスが追加されて、これらは、load()、sendload()、send() というメソッドを持つようだ。
このうち、load() と sendload() については、Javaにおける URLConnectionに相当するものと言え、ヘッダをセットする addRequestHeader() メソッドが用意されている(Flash Player 6以降)。と同時に、やはりJavaアプレットと同様に、同じホストへの接続しか許さないセキュリティモデルが採用されているようだ。
ところが、send() メソッドだけはこれらとは異なる動作をする。これは、getURL()と同じように、ブラウザをジャンプさせる機能(レスポンスをブラウザに表示させる機能)だ。ちょっと歪な設計だと感じるが、この動作を前提としていることからか、send() は getURL() と同様に任意のホストのURLを指定できるようになっている。そして、getURL() と異なるのは、addRequestHeader() メソッドを使ってヘッダをセットできることだ(Flash Player 5のXMLクラスには存在しなかったのが、Flash Player 6で導入されたようだ)。
リクエストヘッダを設定でき、かつ任意のホストへのアクセスを許すという機能は、同類のプラグイン等で他になかったと思うが、さすがにどんなヘッダもセットできてよいとは考えなかったようで、次のように制限されている。
The following standard HTTP headers cannot be added or changed with this method: Accept-Ranges, Age, Allow, Allowed, Connection, Content-Length, Content-Location, Content-Range, ETag, Host, Last-Modified, Locations, Max-Forwards, Proxy-Authenticate, Proxy-Authorization, Public, Range, Retry-After, Server, TE, Trailer, Transfer-Encoding, Upgrade, URI, Vary, Via, Warning, and WWW-Authenticate.
addRequestHeader (LoadVars.addRequestHeader method), Flash Lite 2.x ActionScript Language Reference
ここに Refererが含まれていない。またこの制限は、送信先によらず規定されているようだ。
これは許すべき仕様だろうか。
CSRFは新しい脆弱性ではない。CSRFの名前が付いたのは2001年7月のことで、そのとき既にRefererチェックによる方法も挙げられていた*5。名前が付いたのはこのときだったが、それまでにも漠然とそれなりに知られていた(が、当時はまださほど重大な脅威とは評価されない状況があった*6)のであり、Refererによる対策をとっていたところはけっこうあったと思われる(参考: Bugzilla Bug 141641: disabling cross-site HTTPS referrers breaks sites)。この時点で既に、Refererは偽装させられないのがWebアプリケーションのセキュリティモデルであるという業界コンセンサスがあったと言える*7。
それに対し、Macromedia社が FlashのActionScriptに addRequestHeader機能を追加したのは、Flash Player 6からだから、2002年3月のこと。つまり、後になってやってきたものが、スタンダードを破って登場したということになる。
次に、HTTPのプロトコル規格への準拠性としてはどうなのか。上の会話の中で出てくる (1) の部分を詳しく書くと次のようになる。
HTTP/1.1におけるRefererを規定したRFC 2616の14.36節には、次の記述がある。
14 Header Field Definitions
14.36 RefererThe Referer[sic] request-header field allows the client to specify, for the server's benefit, the address (URI) of the resource from which the Request-URI was obtained (the "referrer", although the header field is misspelled.) (略)
(訳: Referer(原文ママ:"referrer" だが、ヘッダフィールドが綴りを間違えている)リクエストヘッダフィールドは、Request-URIが得られた出所のアドレス(URI)を、サーバの利便性のために、クライアントに明示させることを可能にする。)
The Referer field MUST NOT be sent if the Request-URI was obtained from a source that does not have its own URI, such as input from the user keyboard.
(訳: Request-URIが、それ自身のURIを持たない出所から得られたもの(例えばユーザのキーボード入力のような)である場合には、Refererフィールドは送信してはならない。)
つまり、リンクをクリックしてのジャンプかどうかは規定にないものの、ジャンプ先のURLが書かれているページ がURLとして存在していない限り、Refererを送信してはいけない、つまり、少なくとも、任意のURLを Referer として送信することはしてはいけないことになっている。
そうすると、任意のURLをRefererとして送信してしまう FlashコンテンツやXMLHTTPを使うWebページは、HTTP 1.1に違反した通信をすることになる。だけども、だからといって、そのようなコンテンツを許すブラウザ(およびプラグイン)も、HTTP/1.1 の規格に違反しているとまで言えるかどうかだ。
たとえば、Javaアプレットを想定した場合、Socketクラスを使って任意のTCP通信ができる(ただし接続先はアプレットの置かれているホストのみに制限される)のだから、ここで、HTTP/1.1もどきの通信を実装することができる(アプレットで作られたブラウザ上ブラウザのようなものが実現可能)わけだが、そのプロトコルが HTTP/1.1の規格に違反するものとなることは当然起き得るわけで、それが許されているからといって、Socketクラスの使用を許すブラウザが HTTP/1.1の規格に違反するなどと言えるわけがない。ただし、これは、アプレットの置かれているサイトとの通信に限られた話だ。アプレットの置かれたサーバとクライアントは一体となっているので、アプレットが行う通信はサーバ側の意図に沿うものであるから、サーバ側としてはそれは HTTP/1.1でないことを認めているという考え方もできる。それに対して、アプレットの置かれているホスト以外のホストとの通信に関しては、showDocument() 以外は禁止されているとすれば、HTTP/1.1 の規格は結果として守られていることになる。
このように考えると、HTTPのプロトコル規格の面から見ても Flashの仕様は異端だと言うことができるだろう。
次に、一般的に言うと、ある脆弱性があるときに、修正すべき原因がサーバ側にあると言えるか、クライアント側にあると言えるかが、簡単には切り分けられない場合もある。その最たる例は、10月21日の日記「Session Fixation脆弱性の責任主体はWebアプリかWebブラウザか」に書いた、Session Fixationの問題だ。また、仮に原因がどちらかにあるとしても、両方で対策をとることが強く求められる場合もある。そうしたケースでは、個々の状況に応じて結論が導き出されている。以下にこれまでのそれらの前例を列挙し、どのような理由でどのような方針が出されてきたかを整理してみる。
Cookie Monster問題の解決が簡単でない。Set-Cookie:で指定できるdomain属性のドメインの範囲を、ドメイン登録されるドメインの単位で決定するべきであるが、トップレベルドメイン(TLD)によってその下位のドメイン構造が異なっており、各TLDごとの構造を公表する義務がその管理主体に課されているわけでもないため、公式な構造情報が存在しない。Mozillaプロジェクト内で独自にその構造を表すリスト作りが開始されたが、実用化に至っておらず、今後の成り行きも不明である。
RFC 2965で定められたCookie2を利用すればCookie Monster問題は解決するかもしれない(未確認)が、主要なブラウザで実装されておらず、現時点では現実的な解決策でない。またこの場合、Webアプリケーション側もCookie2を使用するように変更が必要となる。
他方、Webアプリケーション側での対策は比較的容易な場合が多いと思われ、また、かつては元々ログイン後にセッションIDを発行するのが普通だったところ、近年になって、自動セッション管理機能を持つWebアプリケーションサーバの普及とともに、ログイン前からセッションIDを発行するサイトが増えてきたことからこの問題が顕在化してきたという経緯があるので、ログイン後にセッションIDを発行するようにするという方針は自然な面がある。
以上のことから、責任の所在がサーバ側であるかクライアント側であるかが極めて不明確な事例であるものの、サーバ側で対策をとるのが妥当と考えられるようになってきているように思う。
URL上にセッションIDを乗せると、Refererによってリンク先にセッションIDが流出することになるが、リンク先を当該サイト内(もしくは信頼できる関係サイト)のみに限定しているならば流出とは言えず、問題ないとする考え方が存在するところ、SSLによる暗号化で情報を保護しているつもりのページが、セッションIDをURLに乗せており、かつ、http:// のページへのリンクが存在すると、パケット盗聴によりセッションIDを盗まれることになる。
https:// のページから http:// のページへのリンクにおいて、Refererを送出するか否かはブラウザによって異なるが、これは、RFC 2616のSecurity Considerationsの章に次のように書かれていることが影響しているものと思われる。
15.1.3 Encoding Sensitive Information in URI's
Because the source of a link might be private information or might reveal an otherwise private information source, it is strongly recommended that the user be able to select whether or not the Referer field is sent. For example, a browser client could have a toggle switch for browsing openly/anonymously, which would respectively enable/disable the sending of Referer and From information.
Clients SHOULD NOT include a Referer header field in a (non-secure) HTTP request if the referring page was transferred with a secure protocol.
Authors of services which use the HTTP protocol SHOULD NOT use GET based forms for the submission of sensitive data, because this will cause this data to be encoded in the Request-URI. Many existing servers, proxies, and user agents will log the request URI in some place where it might be visible to third parties. Servers can use POST-based form submission instead RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1, 15.1.3 Encoding Sensitive Information in URI's
「SHOUD NOT」であって「MUST NOT」ではないこと、また、Security Considerationsにしか書かれていないことから、規格としてRefererを送出することが禁止されているわけではない。また、次の段落でこれと並置して、sensitive dataをGETで送るようにしないべき(SHOULD NOT)とサーバ側の責任にも触れていることから、ブラウザがその場合にRefererを送っても、ブラウザの脆弱性とみなすコンセンサスはできていないと思われる。
また、https:// ページから他のドメインの https:// ページへのReferer送信については、どのブラウザも送信するようになっている*8ため、「https:// のURLならセッションIDを乗せてもよい」ということにはならない。
したがって、セッションIDをURLに乗せることが許され得るのは、リンク先が当該サイト内(もしくは信頼できる関係サイト)のみに限定されていて、かつ、リンク先が https:// のページである場合だけとなり、ルールとしては複雑なものとなる。
また、(携帯電話向けサイトの場合を除いて)セッションIDをURLに入れざるを得ないとする理由がない。(1990年代にはユーザがcookieの受け入れを拒否するのが流行したが、現在はそうでもない。また、POSTでセッションIDを引き継ぐ方法もある。)
以上のことから、2005年3月当時、URLに埋め込むIDに頼ったセッション管理方式の脆弱性(2)− REFERER情報流出によるセッションハイジャック攻撃の問題 −の開発者向けのアドバイスとして、URLにセッションIDを乗せないことを推奨した*9。
2003年1月に「Cross-Site Tracing (XST) -- The New Techniques and Emerging Threats to Bypass Current Web Security Measures Using TRACE and XSS.」という報告が発表されたが、当時BUGTRAQでは、
I just finished reading this so-called whitepaper and the press release, and all I can say is hyped, sensationalised snakeoil.(略)
System administrators should most definitely not waste their precious time on implementing the silly workarounds suggested, such as disabling TRACE/TRACK requests. (略)
RE: TRACE used to increase the dangerous of XSS., BUGTRAQ, 2003年1月23日
という酷評も出た。なぜなら、TRACE単体で脆弱性になるわけではなく、XSS脆弱性という他の脆弱性が存在する場合にだけそれが起きるという話だったからだ。しかし、全く意味のない指摘ではなく、「Basic認証を使用している場合にXSS脆弱性があるとパスワードが漏れる」という、それまでには知られていなかったリスクが明らかになったものだった(参考 [memo:5237] )。
この発見が当時意味を持ったのは、当時はまだXSS対策が十分に普及しておらず、対策漏れの可能性を無視できない状況があり、その理由からcookieによるログイン管理自体にリスクがあると考えられ、Basic認証の方がより安全であるとする考え方が存在していたからであろう。それが、TRACEメソッドとXMLHTTPのTRACEサポートによって、Basic認証も同様のリスクがあるということが明らかになった。
これ以降、猫も杓子もHTTPサーバでTRACEメソッドを止めるという対策が流行した。脆弱性スキャナによっては、TRACEメソッドが応答するというだけで機械的に「脆弱性」としてカウントするものも現れた。それ自体は脆弱性ではないし、Basic認証を使用していないなら関係のないことなのにだ*10。
そのようになったのは、TRACEメソッドを止めることが極めて簡単であり(Apacheでは最近まで止める設定機能がなかったが)、通常のWebサイトでTRACEは無用の長物だと考えられたからであろう。
ところで、ブラウザからTRACEメソッドでのアクセスを発生させられるようになったのは、このMicrosoftのXMLHTTPが初であったのだから、これは本来、ブラウザ側の脆弱性だとみなすべきものだったかもしれない。つまり、「XMLHTTPでTRACEメソッドを許すべきでない」という主張もできたはずだ。しかしなぜか、最初の報告はそれを提案しておらず、サーバ側で止めることを推奨していた。
そして、IEはその時点ではTRACEを止めることはしなかった。それは、他にXSS脆弱性がない限りそのリスクは生じないからだろう。さらに言えば、最初の報告から数日後の2003年1月26日に「XS(T) attack variants which can, in some cases, eliminate the need for TRACE」という新たな指摘が出た。これは、バーチャルホストを利用しているサイトでは、TRACEを止めていても(XSS脆弱性があるなら)別の方法で同じ結果を招くことを指摘したものだった(参考 [memo:5419])。このことからも、TRACEを無効にするのがスタンダードだとするコンセンサスは得られなかったのだろう。
さらに、2006年1月に「Technical Note by Amit Klein: "XST Strikes Back"」という指摘が出たが、これは、「サーバで止めてもプロキシサーバがTRACEを応答しちゃうよ」という指摘で、他にXSS脆弱性がないかぎり問題でないのは変わらなかった。
しかし、IE 6 SP2ではTRACEを止めている(理由は不明)。そして、Mozillaは2005年に別の明確な理由でTRACEを止めた。今では、TRACEメソッドでリクエストを発行してレスポンスを取得できることが脆弱性であると考えられていると言ってよいだろう。
2000年ごろには、cookieがユーザに毛嫌いされており、cookieを使わないセッション管理の実装が流行しかけていた。後に、URLにセッションIDを乗せる方法は、Refererによって流出するという理由から不適切であるという考え方は普及した。それでもなお、cookieによるセッション管理は危ないとする意見がしばしば見られた。それは、2001年くらいまでは、Internet Explorerにcookieが漏洩する脆弱性がたびたび発見され、しかもなかなか修正されないうえ、Microsoft社が危ないサイトに行かなければ問題ないといった理由で重要視していなかった状況があり、現実問題としてcookie漏洩によるセッションハイジャックの危険性は無視できない状況にあった。そのため、Basic認証やSSLのクライアント認証を使うべきであるという主張もいくらか支持されていた。
しかし、Basic認証やSSLクライアント認証ではログアウト機能を実現できないといった理由から、そうした主張は受け入れられることはなかった。また、後になって、Microsoft社のセキュリティに対する対応の方針転換があり、受動的攻撃でcookieが漏れるタイプの脆弱性(クロスドメイン系の脆弱性)は、同社も危険度の高いものとして扱うようになった。
このような情勢から、現在では、cookieによるログイン管理は普通のものとして評価されるようになり、ブラウザ側のクロスドメイン系の脆弱性は修正が強く求められるようになっている。
HTTPの規格に違反する通信を処理してしまうのが原因のものについては、HTTPサーバ、CGIインターフェイス、プロキシサーバ、ブラウザのそれぞれが、規格に沿うように修正する責任があるだろう。ただし、規格に沿っても解決しない問題もあるそうだ(大岩さん談)。(以下、未整理)
このように、ブラウザ側の脆弱性かサーバ側の脆弱性かが簡単には判断できず、総合的に評価せざるを得ない場合がある。その際に加味されることは、(1)解決策は規格に照らして自然か、(2)解決策は容易なものであるか、(3)解決策がもたらす副作用は十分に小さいか、(4)解決策の実施は現実的であるかなどであろう。
FlashのXML.send()がaddRequestHeader()で任意のサイトに指定のRefererを送信できてしまう今回話題の事例は、これらの微妙なケースと比較すれば、明白に Flash側(またはブラウザ)の責任であると言える。なぜなら、(1)その機能の存在は HTTP/1.1のMUST NOT規定に違反する通信を生じさせるし、(2)それを止めることは容易に実現できる(既製の禁止リストにRefererを追加するだけ)し、(3)止めても何ら不都合はないと思われるし、(4)Flashだけが解決すればよいことで現実的だからである。
このような評価に位置する問題において、「サーバ側の対策ですよ! Flashの仕様ですから!」などと呼びかけることは、製品を修正しないベンダーを認めることにもなることに注意したい。
Amit Klein氏はこれまでに数々の報告、特に、複合的要因によって生ずる問題について、BUGTRAQ等で公表しているが、いずれも、ベンダーに事前に通知した様子が見られない。彼はそういう方針なのだろう。Webアプリケーション側の脆弱性として新たな種類の問題を発見したことにすれば、ベンダーの応答を待つことなく、広く公表して注意を呼びかけることは正当なものとすることができる。明らかにクライアント側の脆弱性と言えるような部類の問題(例えば、XMLHTTPの引数値に対する改行やタブの注入等)であっても、あえてWebアプリケーション側での対応を呼びかけるようなことをしているし、古いバージョンのブラウザを前提(2006年の時点でIE SP1を前提とか)にしたexploitを示して、Webアプリケーション側の対応を呼びかけていたりする。
もちろん、古いバージョンのブラウザを使用しているユーザを想定することが必須の要件である環境(イントラシステムとか)もあるのだから、そうした場合に備えて、回避策としてこうした知識の蓄積も必要である(そうした話題は他にも無数にあるだろう)が、それは、デファクトスタンダードの形成(Referer送信の標準仕様)とは区別して議論するべきことだ。
たしかに、特定の作り方になっているWebアプリケーションの例(A)を挙げなければ、クライアント側のその挙動(B)を脆弱性と指摘できないケースでは、必然的に、(A)が危ないかのような話にもなってしまう。(A)の作り方を止めるべきだとすれば、(B)は直さなくてもよいということにもなる。ここで、全体のデファクトスタンダードのあり方として、どっちを直すのが自然で現実的かということを見極めて示すのが、Webアプリケーション脆弱性の専門家のなすべき仕事だろう。
ましてや、個人的な都合から「Refererはやっぱり駄目なんだ(ということにしたい)」と、感情に判断を左右されるようでは困る。
前回の日記駄目な技術文書の見分け方 その1で、
マスコミの記事でよく見かける表現に、「完全に○○ないわけではない」というものがあるが、これは断定できないことを言うときの逃げ口上だってことを知っておいたほうがよい。
と書いたが、「完全に○○ないわけではない」はもうちょと違う表現じゃなかったか?と思いつつもズバリの表現を思い出せなかった。それを思い出した。「完全に○○ないとは言い切れない」だ。「ないとは言い切れない」でGoogle検索するとマスコミの記事がワラワラと出てくる。
もしや自分も使ってはいないかと、念のため検索したところ、1996年に使っているものが1つみつかった。今の自分では考えられない文章だ。マスコミの文体が伝染していたのだろうか。
*1 またはブラウザのプラグインAPIの脆弱性という見かたもあり得る。
*2 Macromedia(つまりAdobe社)が send() における addRequestHeader() での Refererの指定を許す仕様をどう考えているかだが、Flash Player 9では既に Refererは addRequestHeader()で送信できない仕様になっているようだと報告されている。Flash Player 8以前で送信できる仕様が確認されている(10月に指摘された別の脆弱性であるheader injectionを使わずに送信できる)わけだが、Adobe社は Flash Player 8以前のサポート(脆弱性対応)を中止しているのではないか?
*3 周りの人もちゃんと言ってあげたほうがよいよ。
*4 市場競争の結果として生ずる標準という意味の「デファクトスタンダード」とは少し意味が異なるかもしれない。
*5 これが「The 90% solution: Referer tests」とされているのは、Refererを止めているユーザが使えなくなることを理由としている。
*6 当時は、タブブラウザではなかったし、Windowsも毎日リブートしていたので、ルータの管理者画面などに対するログイン状態が長く続くことはなく、脅威は低く見積もられていた。最近では、ブラウザを立ち上げっぱなしで使うように変化してきたため、そのログイン状態が長く続くようになり、脅威は増した。また、SNSなどのようにログインしっぱなしで使うサイトで、脅威が無視できないサイトが増えてきたという情勢変化がある。(関連: 2005年7月3日の日記「クロスサイトリクエストフォージェリ(CSRF)対策がいまいち進まなかったのはなぜか」)
*7 攻撃者本人による偽Referer送信がセキュリティ対策にならない話と嬉々として混同するせっかちな人はいただろうけども。
*8 Mozillaは一時期これも止めるようにしたが、「銀行のWebアプリが動かなくなる」といった指摘があり、RFCの記述に従って、デフォルトでは送信する(設定で止められる)ように変更された経緯がある。(Bugzilla Bug 141641)
*9 この主張の理由として4番目に、「リンクされていないサーバへのReferer:の誤送出」としてブラウザのバグの可能性を挙げているが、これは補助的な理由にすぎず、これだけの理由ではこの主張をしていない。
*10 私も、TRACEメソッドが応答することをwarningとして出力する検査ツールを開発したことがあるが、Basic認証が使われている場合にだけ出力するようにしている。
「クロスドメイン」っていうと、foo.co.jpからbar.comというように、あるドメイン(にアクセスしている状態)から異なるドメインにアクセスすることだと思ってましたが、違うのでしょうか? 高木浩光@自宅の日記 - 暗黙的に形成する事実標準の話と回避策の話を混同しては....
高木浩光@自宅の日記 - 暗黙的に形成する事実標準の話と回避策の話を混同してはいけない, 駄目な技術文書の見分け方 その1の2 この前書いたのは、読み違えでぼろぼろだったので、もう一度よく考えてみました。 つまり、リンクをクリックしてのジャンプかどうかは規定にない
高木浩光@自宅の日記 - 暗黙的に形成する事実標準の話と回避策の話を混同してはいけない, 駄目な技術文書の見分け方 その1の2 ここで言われているのは、RefererチェックによるCSRF対策が有効であるか否かと言う事ではありません。 でも、結果的にRefererチェックによるCSRF..
うーん,良いウェブメディアの記事もあるけど,この手の記事は基本的に丸飲みにしちゃいけないんですよね.それで昨今のウェブメディアの「クロスサイトスクリプト脆弱性」の解説記事なんか見ると,これそろそろ思考停止してるんじゃないかという気すらしてくるんですわ...