Sage, Sage-Too そして Sage++ の開発系図

■ 自作ソフト Posted by ひぐま (Higmmer) on 2010-02-16 at 22:25:55

◆共有テーマ: Firefox [コンピュータ]

本家Sageから派生したアドオンとしてのSage-Too、そしてSage++ (Higmmer's Edition)がどういう関係にあるのかについていささか混乱が見られるようなので、その開発の経緯を系図にしてみた。

【事実】

  • Sage-Too と Sage++ は共に本家 Sage 1.3.x からの派生アドオン
  • Sage++ は「Sage-Tooの派生アドオン」ではない
  • Sage++ は本家 Sage 1.3.6 の私製改造版として開発がスタートした
  • そこに脆弱性の存在が発覚したため、独自のHTMLフィルタ等のセキュリティ対策を追加した
  • 一方、Sage-Too は本家 Sage 1.3.10 のFirefox3対応版として「Sage 2.0」を名乗る予定だった
  • その時点で本家 Sage は1年以上動きがなく、開発再開は全くの想定外
  • 本家 Sage の脆弱性を掴んでいた私(ひぐま)が Sage-Too プロジェクトに参加してHTMLフィルタ等を提供した
  • ついでに Sage++ の機能の大部分を Sage-Too に移植した(このことが後の開発中止の一因となる)
  • 代わりに Sage-Too のFirefox3対応コードや新機能を Sage++ に逆移植した
  • 本家 Sage 1.3.x/Sage-Too/Sage++ の基本的なコード体系はほぼ同じ
  • 本家 Sage 1.4.x だけが全く独自の進化(退化?)を遂げており、この中では異端の存在
  • 本家 Sage は私が3年前に報告した脆弱性を未だに修正できずにいる

Read Manager for Sage++ 1.3.0 リリース

■ 自作ソフト Posted by ひぐま (Higmmer) on 2010-02-14 at 20:59:06

◆共有テーマ: Firefox [コンピュータ]

本日、Read Manager for Sage++ バージョン 1.3.0 をリリースしました。

Read Manager for Sage++ とは Sage++ (Higmmer's Edition) のユーザースクリプト機能を利用して動作する追加ソフトで、主に以下のような方に効果があります。

  • 更新フィードを開く度に新着だけでなく既読エントリまで毎回表示されるのがうざいという方
  • キーボードだけでサクサクとフィードを読み進められたらいいのに…という方
  • チェックしたエントリを「後で読む」機能が欲しい方
  • フィードをシングルコラムや、もっと美しいレンダリングで読みたいという方

詳しい解説、及びダウンロードはこちらのエントリからどうぞ。

更新履歴

【Version 1.3.0】 2010/02/14

  • 一定期間以上アクセスしていないフィードの履歴を削除する(履歴クリーナー)機能を実装
  • Firefox3以上の環境において"V"キーでエントリを開くとポップアップブロックが作動するのを抑止する機能を追加(有効にするには同梱の「Popup Block Blocker for Sage++ with Read Manager」の導入が必要)
  • UA変更時にデフォルトで仮定するFirefoxのバージョンを3.6に引き上げ
  • 同梱CSSにデフォルトCSSのシングルコラム版を追加
  • extra/gm_scriptsフォルダに幾つかのGreasemonkeyスクリプトを収録

AMOにおける審査とはいったい何なのか

■ 自作ソフト Posted by ひぐま (Higmmer) on 2010-02-09 at 23:17:04

◆共有テーマ: Firefox [コンピュータ]

昨日「Sage 1.4.5 に今も残る深刻な脆弱性について」書いたことを開発者ブログのコメント欄を通じて作者に連絡したところ「再現方法を教えて欲しい」とのことでしたので早速テストファイルを送りました。なので近いうちに修正版がリリースされると思います。と、そんなやり取りをしていた矢先、これまでサンドボックスに入っていたはずの本家Sageが、いつの間にかAdd-ons for Firefox (AMO)のサイト上で通常公開されてしまっていました。

AMO上で Sage 1.4.5 がサンドボックスから出されている様子

AMO上で Sage 1.4.5 がサンドボックスから出されている様子
(2月9日20時現在)

種明かしをすると、昨日のような日本語+(拙い)英語の長文エントリをSageの最新版が公開されたその日に書き上げることができるわけもなく、あれは先月24日にバージョン1.4.4がリリースされてから少しずつ書き進めていたものに1.4.5で更新された内容を加筆したものです。実は1.4.4において脆弱性が修正されていないことはリリース直後に把握していましたが、作者によると「Firefox 3.6 compatibility release following shortly」とのことでしたので公開は保留していました。ですが、恐らくバージョン1.4.5になっても脆弱性が修正されない可能性も十分にあり得ると踏んでいました。そして実際その通りになったわけです。

昨日書いた問題点のうち、不正なリンクURIを使った攻撃については発見者によって脆弱性の実証が可能なテストファイルを含む形で公開されていますし、それ以外に関しても(当時とは若干状況が違いますが)3年前に私がIPAを通じて報告した脆弱性の中に類似のものが含まれています。これが、先のエントリに「一体何を修正して何を確認したのか、大いに疑問を感じざるを得ません」と書いた所以です。

AMOに対する疑問

しかし、それ以上に今回疑問に思ったのは「AMOにおけるアドオンの審査体制はいったいどうなっているのか」ということです。

今回の経緯を見ていくと(かなり推測が入っていますが)、まずWizz RSS News Readerの作者であるMike Kroger氏に対してAMOのレビュアーから「2週間以内に脆弱性の修正のみならず『十分安全』な状態にし、かつ最新のアドオンポリシーに完全に従わなければサンドボックス送りにする」という警告が送られたことに端を発します(*1)。その高飛車な態度に彼はぶち切れてAMOから彼のアドオン(Sage-Tooを含む)を削除してしまったわけですが、本家Sageがサンドボックスに入れられたのも丁度その頃だったように記憶しています。恐らく一方だけを特別扱いするわけにはいかなかったからでしょう。

その後、Mike氏はAdBlock Plusの作者であり、脆弱性の発見者でもあるWladimir Palant氏のアドバイスに従って nsIScriptableUnescapeHTML#parseFragment によるサニタイズ処理を実装するわけですが、その過程で、そのメソッドだけでは数年前に修正したはずの別の脆弱性が再発してしまうことに気づきます(*2)。結果として彼はそのメソッドと独自の処理の組み合わせによって脆弱性を修正したバージョンを彼自身のサイト上で公開しました。

一方、本家Sageの作者は、単純に nsIScriptableUnescapeHTML#parseFragment を採用しました。そして既に報告されていた内容を確認すれば簡単に発見できたはずの脆弱性を見過ごし、それを「security improvements」と称してリリースしました。

――今日現在、前者はAMOから削除され(本人が削除したわけですが)、後者は晴れてサンドボックスから出て通常公開されています

ここから分かることは、AMOのレビュアーと言われる人々は実際にそのアドオンに問題があるかどうかには殆ど関心がなく、もっぱら機械的に「○○メソッドを使っていればOK」「△△メソッドを使っていればNG」という白黒判定しかしていないのではないかということです(*3)。実際、Bugzillaで公開されているファイル[ZIP]を使えば1分で発見できる脆弱性を見過ごしているわけですから。

勿論、星の数ほどあるアドオンをいちいち隅から隅までチェックしろなどと言うつもりはありません。しかし、何かの理由があって一度サンドボックス送りにしたアドオンを再び通常公開するときくらいは、もう少し慎重に検証した方がいいのではないでしょうか。

そして、nsIScriptableUnescapeHTML#parseFragment の問題点を把握してもいないのに他人のアドオンに口出しするのみならず、レビュー方針全般にも影響を与えていると思しきWladimir Palant氏とはいったい何者なのでしょうか。そんなに偉い人なのですか(誰か教えて下さい)。

ましてや広告ブロックは「他人の権利を踏みにじる行為」と考えているMike氏にとって、そんなアドオンを作っている人間から(中途半端に間違った知識で)文句を付けられたことには相当腹に据えかねるものがあったとしても不思議ではありません(勝手な想像です、念のため)。

(*3) なんとタイムリーなことにこんな事例を発見 → Latest topics > evalが危険でそれ以外の方法が安全だと思ってる人へ - outsider reflex

私が Sage++ (Higmmer's Edition) をAMOに登録しない理由

かく言う私は、これまで一貫してこのひまグをSage++の唯一の公開場所としています。Sage++はもともと本家Sageに「フィードを新しいタブで開く」という機能を付け加えたことに端を発し、それが本家Sageに脆弱性が発見されたことでこちらも対応を余儀なくされ、そんなこんなでずるずると続いて今に至るわけですが、元を正せば「他人のアドオンを自分の好きなように改造しただけ (=Higmmer's Edition)」に過ぎないものを、あたかも自分の成果物であるかのようにAMOに登録するのは違うと思ったこと、それがその理由です。

そしてもうひとつの理由として、AMOの中の人やそれに関連する動きに振り回されたくないという思いがあります。今回の一連の動きを見て、その思いを更に強くしました。

ですがユーザーの方にとってみれば「AMOへの登録を拒んでいるひぐまは信用ならん、奴が作ったアドオンなど使いたくない」と思われても仕方ないでしょう。ましてや作者は過去にこんな問題を起こしたこともある人物なわけで、それは当然の判断だと思います。実際、piro氏も指摘している通り、Firefoxのアドオンとは「Firefox自体を改造するパッチ」そのものであり、ひとつ間違えば深刻な問題を引き起こす可能性を常に内包しているわけで、その導入・使用に当たってはどんなに注意してもし過ぎることはありません。

ですから、一般論として、信頼に足ると思われる複数の人物によるチェックを受けたアドオンが登録されていると考えられるサイトを選択するのは賢明な判断です。少なくともこんな野良サイトに転がっている野良アドオンに手を出すことに比べれば、そのリスクはずっと低いでしょう。

ですが、そんなチェックを経たはずの、しかもサンドボックスからも出されているアドオンの中に、既に報告済みの脆弱性が何年にもわたって放置され続けているものが含まれている――という事実は知っておいた方がいいでしょう。

……なんだか書いているうちに「ひぐまは本家Sageを潰したいと思ってる」と受け取られかねない文章になってしまいましたが、そんな意図は毛頭ありません、というか逆に潰れて欲しくありません。膨大な数に上ると見られる本家Sageのユーザー達が乗り換え先を探してSage++に雪崩れ込んできたりでもすればとてもじゃないですが対応できるわけありませんから(^^;

Sage 1.4.5 に今も残る深刻な脆弱性について

■ 自作ソフト Posted by ひぐま (Higmmer) on 2010-02-09 at 01:46:47

◆共有テーマ: Firefox [コンピュータ]

以下で言及している脆弱性は Sage 1.4.6 において修正されましたが
引き続き 適切な対策 を講じることを推奨します (2010年3月10日追記)

本家よりFirefox 3.6対応版の Sage 1.4.5 がリリースされました。開発者ブログによると「セキュリティを改善した」とのことなので早速テストしてみました。

その結果、このバージョンにおいても依然として幾つかの脆弱性が残っていることを確認しました。

Roberto Suggi Liverani氏が報告した脆弱性のうち、link要素内に特殊なURIを埋め込む攻撃に対して依然として脆弱ですし、それどころか、後述のようにエントリ本文に対して従来よりも簡単にスクリプトを埋め込むことができるようにさえなっています。一体何を修正して何を確認したのか、大いに疑問を感じざるを得ません。

公平のために付言すれば、一応セキュリティ面において若干の改善が行われたのは事実のようですが、残念ながらその改善はこれらの脆弱性に対しては効果を発揮していません。一体どこに問題があるのか、ポイントは3つあります。

問題1) nsIScriptableUnescapeHTML#parseFragment は安全か?

Sage 1.4.4 以降では、それまで実装していたオレオレ構文解析器 正規表現によるHTMLフィルタを捨て、Firefox本体に備わっているAPIを使ってエントリ本文のフィルタリング処理を行うように変更されています。コードで示すと以下の通りです。

var sanitizer = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML); var fragment = sanitizer.parseFragment(item.getContent(), false, null, document.documentElement); ds = new XMLSerializer().serializeToString(fragment);

これはAdBlock Plusの作者も推奨している方法で、過去には「JavaScript の除去には nsIScriptableUnescapeHTML を使え(?)」とも言われており、一見何の問題もないように見えます。しかしFirefox 3以降での仕様変更なのかバグなのかは定かではありませんが、現行の nsIScriptableUnescapeHTML#parseFragment は一部の属性(値)内にスクリプトが記述されていてもそれを除去しないようです(*1)。つまりわざわざフィルタをすり抜けるようにフィードに小細工をしなくても正々堂々と(?)スクリプトを注入することが可能となります。はっきり言ってこれでは元のオレオレ構文解析器より部分的には弱くなってしまっています。

ちなみにSage-Too/Sage++では nsIScriptableUnescapeHTML#parseFragment ではなく nsIDOMNSRange#createContextualFragment を使ってDOMツリーを構築していますが、XHTMLの出力前にツリー上の全てのノードを自前で走査してホワイトリストで許可されていない要素・属性を除去する処理を行っています。

(*1) このメソッドが必ずしも安全性を担保しないことはWizz RSS News Readerの作者も指摘しています
[2011年9月4日追記]

この問題はFirefox 3.5.17 / 3.6.14 及びそれ以降のバージョンで修正されました。現在では nsIScriptableUnescapeHTML#parseFragment を使ってスクリプトを正しく除去することができるようになっています。

問題2) そもそもフィルタ処理されない要素がある

また、そもそもの問題としてフィード内の全ての要素が前記のフィルタで処理されているわけではないという問題があります(*2)。その代わり不正な文字などを確実にエスケープすれば問題はないのですが、Sage 1.4.4以前ではそれも不十分でした(Sage 1.4.5で対策が施されました)。

Sage-Too/Sage++でもそれらの部分に対してはDOMフィルタによる処理は行っていませんが、代わりに問題を起こす可能性のある文字をXHTML出力の直前段階で徹底的にエスケープしているのに加え、不正なリンクURIを検出した場合はそれを除去する処理などを加えることで安全性を確保しています。

しかしSage 1.4.5ではリンクURIの検査が不十分なため、依然として特殊なURIを用いた攻撃が可能なままです。

(*2) 処理されない要素 … title、link、author 要素など。
[2010年3月10日追記]

Sage 1.4.6では危険なリンクURIを除去する処理が追加されました。Firefox本体のフィードプレビュー機能が行っているのと同じ方法が用いられています。

問題3) 侵入したスクリプトは chrome 特権モードで動作する

そしてこれが最悪なのですが、以前にも書いた通り本家Sageではchromeスキームで開いたページにフィードを直接書き出しているため、ひとたびスクリプトの侵入を許すと攻撃者はFirefoxの動作を完全に乗っ取り、ありとあらゆる攻撃を行うことが可能となるという重大な危険性があります。

ではどうすればいいのか。 AdBlock Plusの作者は type="content" 属性を指定した browser 又は iframe 要素を使う方法を提唱しています。そのような要素を作成してその中にフィードから生成したHTMLを流し込むようにすれば、たとえそこにスクリプトが混入したとしても、それらがchromeとコンテンツの境界を越えてより上位のウィンドウ、つまりFirefox本体にアクセスすることはできなくなります(*3)。実はこれはFirefox自身が行っているのと同じ方法でもあります。

具体的には "feedsummary.html" を以下のように変更します。

<body> <!-- これを追加 --> <iframe id="feed-frame" type="content" style="border:none;width:100%;height:100%;"></iframe> </body>

そして "feedsummary.js" の "displayFeed" 関数を以下のように変更します。

displayFeed: function (feed) { document.title = feed.getTitle() + " - Sage"; //document.body.innerHTML = CreateHTML.createHTMLSource(feed); document.getElementById("feed-frame").contentDocument.body.innerHTML = CreateHTML.createHTMLSource(feed); },

たったこれだけで悪意あるスクリプトがchrome特権モードで動くことを防止することができます(実験で確認済み)。

但し実際はこの変更を行うとデフォルトCSSが適用されなくなるという副作用がある(コンテンツからchromeパッケージ内のファイルにアクセスできなくなるため)ので更に若干の変更が必要になります。

参考までに、本家 Sage 1.4.5 にそれらの変更を施して一応動作する状態にしたXPIパッケージを以下に置いておきます。但しこれはあくまでもスクリプトの権限昇格を防止するだけであり、スクリプト侵入の脆弱性そのものを修正するものではないので注意して下さい。

注:本バージョンにはユーザーCSSが使用できないという不具合があります
(chromeとコンテンツの境界を越えてローカルファイルにアクセスできなくなったため)

[2010年3月10日追記]

Sage 1.4.6では既知の脆弱性は修正されましたが、上述の type="content" 属性を使用する対策は採用されませんでした。従って、もし未知の脆弱性が存在した場合、攻撃者がそれを利用して悪意あるスクリプトをchrome特権モードで動作させることができる可能性を完全には否定できません

尚、くどいようですがSage-Too/Sage++ではフィードの表示にchromeスキームではなくfileスキームを使用しています(*4)。そのため仮にスクリプトの侵入を許したとしてもそれが特権モードで動作することはありませんが、ローカルファイルの読み取りなど一部の攻撃は可能となる恐れがありますので、心配ならばこのページの情報を見て適切な対策を取ることを推奨します。

(*4) Sage-Too/Sage++は本家Sageの古いバージョン(1.3.x)からの派生アドオンであるため。

>>続きを読む

Sage++ (Higmmer's Edition) 2.2.0 リリース

■ 自作ソフト Posted by ひぐま (Higmmer) on 2010-02-03 at 00:41:54

◆共有テーマ: Firefox [コンピュータ]

本日、Sage++ (Higmmer's Edition) 2.2.0を正式リリースしました。今回のバージョンより Firefox 3.0.xと3.5.xに再対応し、Firefox 3.0~3.6のバージョンにて利用できるようになりました。これらのバージョンにおいて既にSage++がインストールされている場合はアドオンの更新を行うことで自動的にアップデートされます。

手動でのインストールは下記配布ページより行うことができます。

更新履歴 (2.2.0pre2 → 2.2.0)

  • Firefox 3.0/3.5/3.6に対応
  • OPMLインポート直後に更新カウンタが乱れる問題を修正
  • フィード解析における日時の解釈を柔軟化
    • RFC822/ISO8601で解釈できない場合は他方を試みる
    • RFC822においてタイムゾーン指定子"Z"と"JST"に対応
    • ISO8601において
      - 日付と時刻の区切りが"T"でなく空白の場合に対応
      - 時刻が指定されてない場合は"00:00"とみなす
      - 時刻があってタイムゾーンがない場合はローカルタイムが指定されたものとみなす

以前のバージョンにおいて、フォルダ上に更新カウントを表示している状態でOPMLインポートを行った後、すぐに追加されたフィードを開くとそのフィードより上位のフォルダの更新カウントが壊れるという不具合がありました(*1)。この問題は今回のバージョンで修正されました。

また、本バージョンよりフィード解析における日時の解釈をより柔軟に行うようにしました。これによりある程度規格外のフォーマットにも対応可能になりました(*2)。但し、ISO8601においてタイムゾーン(時刻ありの場合は必須)が指定されていない場合にローカルタイムを仮定することには賛否両論ありそうです。本来ならばこのような場合は協定世界時(UTC)として処理するのが正しいかとも思われますが、ユーザーの実使用環境においてはローカルタイムを仮定するのがより現実的と判断しました(*3)。

これによって問題が生じるフィードを発見した場合は作者までご連絡頂ければ対応を検討します。

(*1) フィードを開く前に並べ替えや更新チェックを行った場合は発生しません(だから今まで気付かなかった)。
(*2) 少なくとも作者が購読しているフィードで従来エラーが起こっていたものには全て対応。但し勿論ありとあらゆる場合に対応しているわけではありません。
(*3) というかぶっちゃけ、作者が購読しているあるフィードの仕様に合わせました(^^;

更新判定方法に関する補足

サイトによってはお知らせなどを常にトップに表示させることを意図して未来の日時を設定していることがあるようです。このような場合Sage++では正しい更新判定ができないことがあります(*4)。申し訳ありませんが今の所これに対応する予定はありません。理由は以下の通りです。

  • Sage++では過去に取得したフィードを保持するデータベースを持っていないため、過去との差分で更新判定をすることができない
  • フィードから生成したシグネチャを比較する方法で更新判定を行うこともできるが、更新されてないのに更新されたと逆に誤判定する可能性がある

フィード毎に更新の判定方法をカスタマイズできるようにすることも一時検討しましたが、作者がそこまでの必要性を感じていないため手付かずとなっています。

ちなみに現在のSage++においても、フィード内の各エントリが日時情報を持っていない場合は代わりにタイトルから生成したシグネチャを比較する方法で更新判定を行っています。そこでYahoo! Pipesでこんなものを用意しました。

フィードをこのPipeに通すと各エントリの日時情報が除去されます。つまりこの出力結果をSage++で購読すれば、日時ではなくタイトルの更新を検出して更新判定を行うようになるはずです。残念ながら作者は具体的なフィードの実例を知らないので意図通り動くかどうかは確かめていませんが、もしよかったら試してみて下さい。

(*4) いつも同じ(未来の)日時が設定されている場合など。

>>続きを読む