2013-05-19
■[SECURITY] 補足編:機密情報を含むJSONには X-Content-Type-Options: nosniff をつけるべき

「機密情報を含むJSONには X-Content-Type-Options: nosniff をつけるべき - 葉っぱ日記」の補足編です。
- 結局、よくわからないんだけど。
- よくわからない場合は、とにかく全てのレスポンスに X-Content-Type-Options: nosniff をつけましょう。
- 機密情報を含むJSONにX-Content-Type-Options:nosniffをつける理由はわかったけど、「あらゆる」コンテンツにつける理由はなぜ?
- 機密情報を含まなくても、<script>のような文字列を含むコンテンツをIEで直接開いた場合にはXSSにつながる可能性もあります。どのようなコンテンツにX-Content-Type-Options:nosniffが必要かを考えるくらいであれば、全てのコンテンツに付与したほうが間違いがなくていいでしょう、ということです。
- IEのためだけの問題でしょ。IEが対応しろよ。
- 確かに現在明確に問題があるのはInternet Explorerだけですが、「機密情報を含むコンテンツをスクリプトなどのリソースとして読み込み、その機密情報に攻撃者がアクセス可能になる」という種類の脆弱性はこれまでにもIE以外にもたびたび発見されています。また、現在X-Content-Type-OptionsレスポンスヘッダによるContent-Typeの厳格化はIE以外のブラウザにも導入される動きがあります(Heads up: nosniff header support coming to Chrome and Firefox · GitHub:)。そのような状況を考えると、X-Content-Type-Options:nosniffを付与することでコンテンツ内の機密情報の盗み見を防ぐということは、IE以外のブラウザに対しても現在発見されていない脆弱性・攻撃手法を防ぐという点で備えとして十分意義のあるものだと言えます。
- XMLHttpRequestからリクエスト発行時に X-Request-With のようなカスタムヘッダを付与する方法で防げるのでは?
- はい。PHPのイタい入門書を読んでAjaxのXSSについて検討した(3)〜JSON等の想定外読み出しによる攻撃〜 - ockeghem(徳丸浩)の日記 に示されるような方法で、XMLHttpRequest からJSONを取得するリクエスト発行時に xhr.setRequestHeader( "X-Request-With", "XMLHttpRequest" ); のようにカスタムのリクエストヘッダを付与し、JSONを提供するサーバ側ではそのヘッダの有無を確認するという方法で、リクエストをXMLHttpRequestからのみに制限し<script>要素による読み込みを防ぐことができます。JSON提供側がXMLHttpRequestからのみを想定している場合に、実際にXMLHttpRequestにのみ応答を制限するという方法は、今回の攻撃に限らず様々なAjaxレスポンスを利用した攻撃に対しても有用な対策方法となりますので、ぜひそうすべきです。
- CSRFトークンがついていれば防げるのでは?
- はい。JSONのリクエストに攻撃者の知りえないCSRFトークンが必要ということであれば、<script>要素として読み込むことはできないため、今回示したような攻撃は防ぐことができます。ただし、一般的にはCSRFとはリクエストが発行された時点でのサーバ側での副作用を狙った攻撃のためGETではなくPOSTになることがほとんどで、JSONの発行がPOSTのみに制約されている場合にはそもそも<script>要素でJSONを読み込むことはできません。
2013-05-17
■[SECURITY] 機密情報を含むJSONには X-Content-Type-Options: nosniff をつけるべき

WebアプリケーションにおいてJSONを用いてブラウザ - サーバ間でデータのやり取りを行うことはもはや普通のことですが、このときJSON内に第三者に漏れては困る機密情報が含まれる場合は、必ず X-Content-Type-Options: nosniff レスポンスヘッダをつけるようにしましょう(むしろ機密情報かどうかに関わらず、全てのコンテンツにつけるほうがよい。関連:X-Content-Type-Options: nosniff つかわないやつは死ねばいいのに! - 葉っぱ日記)。
例えば、機密情報を含む以下のようなJSON配列を返すリソース(http://example.jp/target.json)があったとします。
[ "secret", "data", "is", "here" ]
攻撃者は罠ページを作成し、以下のようにJSON配列をvbscriptとして読み込みます。もちろん、JSONをvbscriptとして実行はできずエラーが発生するので、エラーハンドラも設定しておきます。
<!-- 攻撃者の用意した罠ページ --> <script> window.onerror = function( e ){ alert(e); }; </script> <script src="http://example.jp/target.json" language="vbscript"></script>
被害者がこの攻撃者の用意した罠ページを訪れたときには、JSON配列がVBScriptとして読み込まれ、実行時エラーが発生します。
エラーハンドラには、VBScriptエンジンの生成したエラーメッセージが渡されますが、そこにはJSON配列内の文字列がそのまま含まれますので、攻撃者はJavaScriptから自由にそれらの機密情報にアクセス可能となります。
この脆弱性は、昨年私とmasa141421356さんとで発見し、Microsoftへ連絡しました。無事、今月リリースされた マイクロソフト セキュリティ情報 MS13-037 - 緊急 : Internet Explorer 用の累積的なセキュリティ更新プログラム (2829530)にて「JSON 配列の情報漏えいの脆弱性 - CVE-2013-1297」ということで修正へと至ったのですが、実際にはMS13-037にてこの問題が修正されるのはInternet Explorer 6、7、8だけであり、Internet Explorer 9および10については依然として上記の方法でリモートのJSON配列を攻撃者は読み取り可能なままです*1。
Internet Explorer 9、10に対しては、JSON配列の生成において X-Content-Type-Options: nosniff レスポンスヘッダを付与することで攻撃を防ぐことができます。 X-Content-Type-Options: nosniff レスポンスヘッダが付与されたコンテンツを<script>などで読み込む際には、Internet Explorer 9、10ではContent-Typeが厳密に確認され、VBScriptを示すContent-Typeが付与されていないリソースをVBScriptとして読み込むといったことができなくなるためです。
ちなみに、IE6-8の場合であっても同一オリジン上に配置されるJSON配列に関しては、MS13-037適用後も上記のvbscriptエラーを利用した方法でJSON配列の内容を読み取ることができます。
なお、CVE-2013-1297についてMS13-037ではIE9、10が修正されておらず X-Content-Type-Options による保護が必要となる点については、Microsoft に問い合わせたところ仕様に基づく動作との回答を得ています。
■ まとめ
- リソースが機密情報を含む場合はもちろん、そうでない場合にも全てのリソースに対して X-Content-Type-Options: nosniff をつけるべきでしょう。
- MS13-037ではCVE-2013-1297が修正されるのはIE6-8のみである。
■補足
ブコメやTwitterでの反応をもとに補足編も書きました。
*1:National Vulnerability Database (NVD) National Vulnerability Database (CVE-2013-1297)などでは対象としてIE9、10は書かれていませんが。
2013-03-30
■[HTML5][SECURITY] Same-Origin Policy とは何なのか。

ちょっと凝ったWebアプリケーションを作成していたり、あるいはWebのセキュリティに関わっている人ならば「Same-Origin Policy」(SOP)という言葉を一度は聞いたことがあると思います。日本語では「同一生成元ポリシー」あるいは「同一生成源ポリシー」などと訳されることもありますが、個人的には「オリジン」は固有の概念を表す語なので下手に訳さず「同一オリジンポリシー」と書いておくのが好きです。
さて、この「オリジン」とは何なのかという話ですが、これは「RFC 6454 - The Web Origin Concept」で定められており、端的に言うと「スキーム、ホスト、ポート」の組み合わせをオリジンと定め、それらが同じものは同一のオリジンとして同じ保護範囲のリソースとして取り扱うということです。
例えば、http://example.jp/fooとhttp://example.jp:80/barはそれぞれ同一のスキーム(http)、ホスト(example.jp)、ポート(80)を持つため、同一のオリジンであると判断されます。一方、https://example.jp/fooとhttp://example.jp/fooはどちらも同じホストを持ちますが、スキームがそれぞれhttpsとhttpであるため、異なるオリジンのリソースであると判断されます。data:スキームはそれぞれ独立したオリジンを持つものとして取り扱われ、またfile:スキームに関しては実装依存ということになっています。2つのオリジンが同一でない場合、すなわち異なるオリジンを「クロスオリジン」と言います。
RFC6454では、現在のドキュメントのオリジンを正規化して表現する方法も定められており、例えばhttp://example.jp:80/foo/bar/というリソースであればオリジンは正規化するとhttp://example.jpとなります。最近のWebKit系のブラウザでは、表示しているドキュメントのオリジンを示す location.origin というプロパティが実装されています。
オリジンに依存して動作が制約されるものとしては、以下のようなものがあります。
- XMLHttpRequest
- 同一オリジンでは無条件にレスポンスを読むことができますが、クロスオリジンでは許可された場合にしかレスポンスにアクセスすることはできません。
- Canvas
- Canvasに表示された画像は、同一オリジンであれば画像データにJavaScriptからアクセスすることができますが、クロスオリジンの場合には許可された場合にしかアクセスできません。
- Web Storage
- データの保存される単位はオリジンに基づきます。クロスオリジンでのデータの読み書きはできません。
- X-Frame-Options
- SAMEORIGINのような指定をした場合には、オリジンを超えてフレーム内にコンテンツを含めることはできません。
これら以外にもWeb上の様々な挙動について、対象が同一オリジンの場合には制約なしにリソースの読み書きが可能となるのに対し、クロスオリジンの場合には原則としてリソースの読み書きが禁止になることを「同一オリジンポリシー」と言います。
一方、オリジン以外をベースに動作の制約に定めているものとしては以下のようなものがあります。
- Cookie
- Cookieはpathやdomain指定が可能であり、またデフォルトではhttpとhttpsで共有されます。
- HTTP認証
- HTTP認証はpath名を元に認証情報を送信するなど、オリジンに基づかない動作となります。
- document.domain
- JavaScriptでdocument.domainを書き換えることによりホストを超えてinnerHTMLなどの読み書きが可能になります。
XMLHttpRequestやCanvasなどのオリジンにより制約を受ける機構において、クロスオリジンでのリソースでアクセスする方法については、Cross-Origin Resource Sharing(CORS)がルールとして定められています。CORSに基づいてリクエスト、レスポンスを発行することにより、XMLHttpRequestやCanvasにおいてクロスオリジンでリソースにアクセスすることが可能になります。CORSについては、「HTTP access control | MDN」が日本語でよくまとまっていますので参照するとよいと思います。
まったくの余談になりますが、「クロスサイトスクリプティング」という攻撃手法の名前について「何が『クロスサイト』なのか分からない」という声をよく聞きますが、ここでいう「サイト」は純粋な意味でのサイトではなくオリジンを示しているのであり、クロスサイトスクリプティングはオリジンを超えてスクリプトを注入する攻撃だと考えると、わりとしっくり来るような気がします。
2013-03-29
■[SECURITY] IPAから「クリックジャッキング」に関するレポート出ました

Webアプリケーションのセキュリティの分野で「新しい攻撃手法」は実はそれほど多くないのですが、比較的新しく対応が求められているものとしてクリックジャッキングがあります。クリックジャッキングは、CSRFと同じように「Webアプリケーションのサーバー側機能」を利用者(被害者)に実行させる手法です。CSRFは、当該機能を実行するHTTPリクエストを送信させる罠を使いますが、クリックジャッキングの方は、iframe等に当該機能を呼び出す画面を表示しておき、利用者(被害者)に実行ボタンを押させる(=クリックジャック)手法です。クリックジャッキングに関しては4年前にすでにJPCERT/CCが詳しい解説を出していたのですが、この3月26日にIPAからも「クリックジャッキング」に関するレポートが公開されました。
X-FRAME-OPTIONSについて、このレポートには記載されていない点についていくつか補足しておきます。
SAMEORIGINは同一ドメインではなく同一オリジン
IPAのレポートでは、X-FRAME-OPTIONSの値としてSAMEORIGINを指定する場合の注意として
SAMEORIGINを選択する場合で注意しなければならないのは、同一ドメインのみである点だ。
と書かれていますが、SAMEORIGINは言葉通り同一オリジンの場合のみフレーム内で表示されることを想定しており、httpとhttpsなどのようにプロトコルが異なる場合や、同一プロトコルであってもポートが異なる場合には異なるオリジンとなりますので、フレーム内でも表示されません(もしかすると表示されるブラウザが存在するかもしれませんが、少なくともそれは仕様を逸脱した挙動です)。
SAMEORIGINやALLOW-FROMの指定はフレームを指定している親ドキュメントではない
X-FRAME-OPTION: SAMEORIGINやX-FRAME-OPTION: ALLOW-FROM uriを指定した場合に、制約を受けるベースとなるのはiframeやframeを記述した親ドキュメントではなく、「トップレベルブラウジングコンテキスト」すなわちトップレベルのドキュメントになります。例えば、http://example.jp/top.html からiframeでhttp://evil.example.com/fake.htmlのコンテンツを埋め込み、http://evil.example.com/fake.htmlからiframeでhttp://example.jp/child.htmlのコンテンツを埋め込んだ場合、http://example.jp/child.htmlがX-FRAME-OPTION: SAMEORIGINを指定していたとしてもiframe内にコンテンツは表示されることになります。
FirefoxにおけるALLOW-FROMの指定はfalse-positiveが多い
IPAのレポートでは、X-FRAME-OPTION: ALLOW-FROM uriをサポートするブラウザとして Firefox 19.0 を挙げています。たしかに Firefox ではALLOW-FROMをサポートしているのですが、指定方法には癖があり、間違った指定をした場合にフレームでの表示をブロックせずにそのまま表示してしまうという問題があります。
例えば http://example.jp 上のコンテンツで X-FRAME-OPTION: ALLOW-FROM として様々な値を指定した場合、IEと比べると下表のようになります。
| X-Frame-Optionsの値 | 期待される挙動 | IE8-10 | Firefox |
|---|---|---|---|
| ALLOW-FROM:example.jp | ブロック | ブロック | 表示 |
| ALLOW-FROM: example.jp | ブロック | ブロック | 表示 |
| ALLOW-FROM example.jp | ブロック | ブロック | ブロック |
| ALLOW-FROM:http://example.jp | ブロック | ブロック | 表示 |
| ALLOW-FROM: http://example.jp | ブロック | ブロック | 表示 |
| ALLOW-FROM http://example.jp | 表示 | 表示 | 表示 |
| ALLOW-FROM:example.com | ブロック | ブロック | 表示 |
| ALLOW-FROM: example.com | ブロック | ブロック | 表示 |
| ALLOW-FROM example.com | ブロック | ブロック | ブロック |
| ALLOW-FROM:http://example.com | ブロック | ブロック | 表示 |
| ALLOW-FROM: http://example.com | ブロック | ブロック | 表示 |
| ALLOW-FROM http://example.com | ブロック | ブロック | ブロック |
なお、上記の表でALLOW-FROMの後ろにコロンをつけた指定をテストしているのは、IETFのドラフト01では ALLOW-FROM: uri のようにコロンを指定するよう定められていたからです(現在のドラフト02では不要です)。
(今日の参考文献: IPAから「クリックジャッキング」に関するレポート出ました | 徳丸浩の日記)
2013-03-10
■[HTML5][SECURITY] Web StorageやindexedDBを扱う上でのセキュリティ上の注意点(続編)

先日書いた「Web StorageやindexedDBを扱う上でのセキュリティ上の注意点」の続き。
sessionStorage を使うと解決するか
この用途なら sessionStorage でよい (はてなブックマーク - ssig33 - 2013年3月9日)
sessionStorageはウィンドウあるいはタブが開かれてから閉じるまでの間をひとつのセッションとして管理し、そのセッションの期間中だけデータを保持します。ウィンドウやタブを閉じる際にはセッションは終了し、ストレージ上のデータは破棄されます。通常のWebアプリケーションでは、ログイン/ログアウト間というアプリケーション側が想定しているセッションと、sessionStorageのいうところのセッションとは異なる概念であり、ウィンドウを閉じる前に異なるユーザでログイン/ログアウトを繰り返した場合には同種の問題が発生する可能性がありますので、根本的な解決にはなりません。
機密情報の取り扱い
@ockeghem: ここで議論している前提条件では、秘密情報をlocalStrageに保存してはいけない、というのが正解ではないかな? Web StorageやindexedDBを扱う上でのセキュリティ上の注意点 - 葉っぱ日記 URL
2013-03-09 12:19:19 via web
同じデバイス上のブラウザを複数のユーザで共有して使用する場合、Web StorageやindexedDBに限らず、閲覧履歴や通常のキャッシュ、Cookieなど様々なリソースにそれらの複数ユーザがアクセス可能なことがありますが、Webアプリケーション側でどこまでそういった状況に対処するのかは様々な考え方があるように思います。例えば、そういう状況に関してはOS/ブラウザのユーザ管理機構や最近であればブラウザのプライベートブラウジング機能、あるいはユーザ自身の手によるプライベート情報の削除に期待することでWebアプリケーション側では複数ユーザがブラウザを共有するという状況に対応しないという考え方もあるでしょうし、機密情報を含むあらゆるコンテンツのキャッシュの禁止はもちろん、location.hashにも機密情報となりえる情報を含まないことを徹底することで、複数ユーザがブラウザを共有した場合でもリスクを最小限にするという考え方もあると思います。後者のような考え方であれば、もちろんWeb Storage、indexedDBにも機密情報を含めるべきではないということになると思います。(通常、そもそもあまりこういう条件については考慮しないと思います。)
では、機密情報さえ保存しなければ前回書いた記事のようなWeb StorageやinedxedDBにおけるセキュリティ上の問題は全て解決するのかというと、そうでもないように思います。あまり適切な例ではありませんが、例えば「このデバイスからは常に位置情報を送信する」のような設定値をlocalStorageに保存している場合、そのユーザの公開プロフィール画面や公開されている発言などを見ると外部からもどちらに設定しているかが推測可能であって(ようするに機密情報ではない)、とはいえそのような設定値をログアウト/ログインをまたぐことで複数ユーザ間で混同して使用されてしまった場合には大きな問題となり得ます。そうなってくると、結局のところ、「機密情報を保存してはいけない」ではなく「ユーザに紐づいたデータを保存してはいけない」になってしまうように思います。
現在、Web StorageやindexedDBの恩恵を最大限に受けている種類のソフトウェアのひとつとして、GreaseMonkeyやブラウザ拡張のようなサーバサイドの協力を得ずにクライアント上の処理だけで特定の問題を解決する類のソフトウェアがあるのではないかと思っています。このような種類のソフトウェアに対して、ユーザに紐づくデータをクライアントサイドのストレージに保存してはいけないという制約を課してしまうと、圧倒的に作成可能な幅が狭まるのではないかという気もしないでもないです。
「クライアントサイドのストレージに機密情報を保存してはいけない」という言葉が、そもそも保存すること自体が脆弱であるということなのか、あるいはセキュアに実装するための手段が存在しないから手法そのものを禁じ手としてしまうということなのか、それとも、脆弱性を引き起こしやすいのでしないほうがいいというレベルなのか、徳丸さんの言われていることについて真意がもう少し知りたいなと思いました。

