いくつかのGreasemonkeyスクリプト or 拡張機能に対してセキュリティに関するバグ報告をしました。作者が公開している最新版に更新しましょう。
いくつかの点に置いて、公開の場での議論が適切だと思っています、その際に具体的な悪用方法についても触れられる可能性があります。アップデートしてない人はなるべく早くアップデートしましょう。AutoPagerizeで一部のサイトが動作しなくなったという場合は正常です。全部のサイトが動作しなくなったという場合はGreasemonkeyのユーザースクリプトコマンドからAutoPagerize - clear cacheを試しましょう。
概論
ユーザーが特定のタグ、class属性などに対応してイベントが発火するようになっている拡張機能をインストールしていて、さらにそれが外部のコンテンツをロードしたりする場合、拡張機能側でセキュリティに対する配慮が欠けていた場合、そのWebサイト上で本来想定していない任意のscriptが実行される危険があります。市民権を得ているメジャーな拡張機能の場合、Webサイト運営者から「使わないでください」と呼びかけるのは現実的ではないので、拡張機能の作者に対応を求めたり、ユーザーにアップデートを働きかけることが必要だと考えています。
発端は別ドメインのtumblrが継ぎ足されるというバグ報告がtwitter上であったこと。これは(*.tumblr.comではあるが)別ドメインのコンテンツが読み込まれていることになるので、単なるバグではなく「セキュリティ上の問題だな」と考えた。
つまり
- ある程度自由にタグが書ける(任意のURLに対するリンクを貼れる)サービスでは、次ページのリンクの判定処理が誤爆することがある。
- 誤爆した場合、ユーザーとサイトオーナーが意図した挙動に反して、別ドメインのコンテンツが読み込まれて継ぎ足されることになる。
- この際に継ぎ足したコンテンツにscriptが混入していると、そのドメイン内で本来意図していないscriptが実行されることになる。
そもそも別ドメインのコンテンツを読み込む必要性があるか
- XMLHttpRequest(クロスドメイン不可) から GM_xmlhttpRequest(クロスドメイン許可) に変更された。
- 何故こうなったのかというと、正確な時期と経緯は忘れたが「次ページが別ドメインになっているサイト」というのが稀に存在するためだったと記憶している。
- 代表的なのはYahooで、あらゆるリンクにリダイレクタが挟んであるので、次ページのリンクの検出を同一ドメインに限定すると動作しなくなる。
- XHR level2の登場により、サーバー側で設定すれば異なるホストからも読み取りが許可されるようになった。
- このため、user.js側でURLを読み取る際に同一ドメインであるかどうかのチェックが必須になった
AutoPatchWorkのケース
- AutoPatchWorkでは、同一ドメインであればXMLHttpRequestを使って読み込み、別ドメインの場合IFRAMEを使ってロードするという挙動になっている。
- これはリンク先がリダイレクタになっていて、元のドメインに戻ってくるようなケースでコンテンツを読み取ることが出来る、という挙動を意図して書かれているのだろう。
- これによって、別ドメインの読み取りを基本的に禁止しつつ、結果として同じドメインに戻ってくるリダイレクタであれば読み取りが許可される。
- これはそもそも、そのドメイン上に表示することを目的としているので、読み込んだHTMLからscriptやイベントハンドラの記述を取り除く必要がある。
- 元々、scriptを除去する処理が加えられていたが(詳細は公開しないが)抜け道がある状態だったのでバグ報告をした。
傾向と対策
- A: クロスドメインを禁止する対策を入れる
- 読み取りを同一ドメインからのみに限定するような処理を入れる
- そもそも読み込み先のドメインが同一であるかどうかを確認する + リダイレクト先を追わないようにする。
- 最終的に取得したコンテンツのドメインが、現在表示しているlocation.hostと同一になるかを調べる。
- 実装方法としては: GM_xmlhttpRequest+finalUrlをチェック、AutoPatchWork方式、XMLHttpRequest+リダイレクトされてないかチェック(方法があれば)
- B: scriptが混入しないようにフィルタをかける
- そのドメイン上に外部ドメインのコンテンツを表示する場合は必須
- これについてはあとでかく。
Q: GM_xmlhttpRequestをXMLHttpRequestに置き換えれば安全なのか?
- No.
- XHR level2によって「XHRは同一ドメイン上のコンテンツしか読めない」という前提が崩れている。
- 同一ドメインしか読めない前提で設計されているGreasemonkeyや拡張機能は、危険な挙動になっている可能性がある。
- 任意のURLを読み込む機能があるが、特に読み込み先のURLをチェックせず「XHRで例外が発生する」ことに依存している、というケース。
- これは拡張機能だけではなく、例えばクエリパラメータでURL(またはURLの一部)を受け取って、Ajaxで任意のコンテンツを読み込むような設計のウェブサイトに影響がある。
- もしバリデーションなしに任意のURLを読み込んで、HTMLとして描画する機能を備えていた場合、悪意のあるscriptが混入する可能性がある。
Q: 同一ドメイン上に任意のコンテンツを表示できる場合はどうか?
- 同一ドメイン上のコンテンツを、オーナーの意図しない形で継ぎ足すことによって危険な挙動になることも考えられる。
- ただしこれは無視しても良いケースであると考えている。
- なので同一ドメインのコンテンツを継ぎ足す場合は、基本的にscriptの除去等は行わなくて良いだろう、と考えている。(速度面でデメリットがある、onclickなど生きていたほうが嬉しいケースがある)
- httpsのページにhttpのコンテンツを継ぎ足すのは避けたほうが良いだろう。
Q: 同一ドメインにオープンリダイレクタがあった場合にはどうか?
- 拡張機能側で同一ドメインのリンクしか読まないように設計されていた場合でも、そのドメイン内にオープンリダイレクタがあった場合に任意のコンテンツが読み込まれる可能性がある。
- リダイレクト先がクロスドメインでの読み取りを許可してあった場合はどうなるか?リダイレクタ経由で外部ドメインのコンテンツを読み込むことが出来るか?
- Firefoxは可能、Chromeだと失敗した。
- そのため、Chrome拡張のAutoPatchWorkでは動作しなかった。
- 明文化された仕様なのかどうか、Webページ上と拡張のコンテキストで挙動が違うかどうか、などについては調べていない。
- そもそも認証機能を扱うようなドメインにはオープンリダイレクタが存在しない方が望ましいと考えているので、サイト側の問題であるとも言える(20%ぐらい)
課題
- XMLHttpRequestを使う場合、クロスドメインリクエストを禁止(もしくは判定)するスマートな方法が現状、ないっぽい。
- ヘッダ取れるかどうかとかで判定できるかと思ったけどAccess-Control-Allow-Headersで挙動が変わるぽい。
- 仕様的にはリクエストURLをチェック + req.followRedirects = false でイケルはずだがfollowRedirectsは現行のブラウザはまだ対応していないようだ。
- GM_xmlhttpRequestのresponseには、finalUrlがあるので、これをチェックすることで同一ドメインかどうか判定できる。
- AutoPagerizeの場合、誤爆を防ぐための仕組みが拡張機能側で必要ではないか?
- 不適切な記述のSITEINFOを拒絶する、複数マッチした場合は同一ドメインのものを優先する、etc。