Sage++に搭載されているHTMLフィルタに関する技術的補足

■ 自作ソフト Posted by ひぐま (Higmmer) on 2007-01-30 at 02:04:41

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

本エントリは「Sage++ (Higmmer's Edition)の高速HTMLフィルタの脆弱性対策について」に関する技術的補足です。

拙作の「Sage++ (Higmmer's Edition)1.3.9以前では「高速」「詳細」の2種類のHTMLフィルタを搭載していました(下線部追記)。両者の違いについてこちらのページでは以下のように説明しています。

  • 高速 … 公式版Sage 1.3.7以降で導入されたHTMLフィルタ。内蔵のブラックリストに一致する要素/属性を遮断する(それ以外は許可)。動作は比較的高速。
  • 詳細 … Higmmer's Edition独自方式。許可する要素/属性等を指定するホワイトリスト方式(CSSフィルタはブラックリスト方式)。フィルタの設定はカスタマイズ可能だが動作はやや遅い。

ブラックリスト方式かホワイトリスト方式か、或いはカスタマイズ可能か否か、というのも確かに相違点ではあるのですが、技術的に見た場合に両者が根本的に異なっている要素があります。それは、テキストベースのフィルタか、DOMベースのフィルタかということです。その観点から見た場合、両者の動作は次のように説明できます。

  • 高速 … テキストベースのHTMLフィルタ。正規表現に基づいて構文解析を行い、ブラックリストにマッチした要素/属性を削除する。
  • 詳細 … DOMベースのHTMLフィルタ。入力HTMLをDOMパーサで構文解析してドキュメントツリーを構築、ホワイトリストを参照しながらDOMノードを走査して不要な要素・属性をツリー上から削除する。その後、残ったツリーを(X)HTMLに変換して出力する。

これで詳細HTMLフィルタが遅い理由が分かって頂けたと思います(*)。

(*) ちなみに詳細HTMLフィルタがノードを削除・置換する様子は
about:config で "sage.filter_debug_mode" を true に設定することで観察することができます。

一見するとテキストベースの正規表現に基づく方式の方がシンプルに書けて動作速度上も有利なように思えますが、そこに大きな落とし穴があります。それは「正規表現で書かれた構文解析器と、ブラウザに内蔵されている構文解析器の動作は同じではない」ということです。便宜上、正規表現で書かれた構文解析器を「オレ構文解析器」と呼ぶことにします。

フレッシュリーダーの中の人のブログで、そのような違いが引き起こす危険性の一例が紹介されています。

<a href="javascript:alert('Hello')>click here</a>

例えば上記のようなソースがあった場合、href属性の記述が閉じられていないためオレ構文解析器では正しく処理されず、そのままスルーしてしまうおそれがあります。しかしFirefoxの構文解析器はこのような多少の構文エラーがあった時でも「親切(お節介?)にも」適当に間違いを補完してしまうのです(*)。

(*) 実際にSage++の高速HTMLフィルタモードで試した結果、そのままでは動きませんでした。
しかし……(検閲削除)

勿論このような構文エラーにも対応した正規表現ルールを追加すればこの種の脆弱性は回避できます。しかし、Firefoxが行ってくれる「親切な補完」は数多く存在します。事実Sage 1.3.8から1.3.9への更新で似たような脆弱性のひとつが修正されたにも関わらず、このテスト結果からも分かるように、まだまだ同じような脆弱性が残っているようです。1.3.10でもまだ残っています(追記)

この「2つの構文解析器のスキマ」を突いた新しい攻撃方法が判明する度に、それに対応した正規表現ルールを追加するというような対症療法的なやり方ではあまりにも効率が悪すぎます。そもそもHTML文書の構文解析に単純な正規表現ルールを用いてしまったところに無理があったのだと思います。

一方、私がSage++で独自に実装した詳細HTMLフィルタでは、Firefoxに搭載されているDOMパーサにより構文解析を行っています(*)。誤解を恐れずに喩えるならFirefoxのレンダリングエンジン(Gecko)が行う構文解析の動作を内部的にシミュレートしているようなものだと言えるでしょう。

(*) 基本的にはquaaさんのページで解説されているのと同じ手法を用いています。

全てのフィルタ処理は構文解析の結果出来上がったDOMツリー上で行われます。仮に構文エラーがあったとしても「親切な補完」の後で処理されるのでフィルタをすり抜けることはありません(たぶん)。フィルタ処理が終わったら残ったDOMツリーを(X)HTMLに変換して出力します。その結果はXML的に"well-formed"であり、もはや構文エラーやエスケープ漏れが入り込む余地はない(はず)です。Firefoxがレンダリング時に行う「親切な補完」を気にする必要がないのです。実際、先ほど紹介した例に関しても「補完後」のノードに対してきちんとフィルタが適用され、出力HTMLにスクリプトが混入することはありませんでした。

なるほどそれなら良いこと尽くめじゃないかと思われるかも知れませんが、そうとも限りません。ひとつめの問題は、詳細HTMLフィルタで用いているDOMパーサの動作と、Firefoxが実際のレンダリング時に行う構文解析の動作が完全に同じとは限らないということです。もし両者の間に僅かでも「スキマ」があった場合、それが脆弱性の引き金になる可能性は否定できません。

ふたつめは、フィードに含まれる全ての要素(内のデータ)がHTMLフィルタにかけられているわけではないということです。詳細は省略しますが、万一それらの処理に欠陥があった場合、そこを突いた攻撃が可能になる可能性もあります。

そしてもうひとつ、ここで述べたことはスクリプトインジェクション系の脆弱性対策についてだけであり、その他の脆弱性の有無を検討する上では全く的外れであるということです。例えばバッファオーバーフローや不正パラメータによる攻撃、DoSアタック等、ありとあらゆる脆弱性の可能性を検討して対策する時間も能力も私にはありません(残念ながら)。

従って、詳細HTMLフィルタを使用したからといって100%安全であるという保証はどこにもありません!!
案の定新たな脆弱性が見つかってしまいましたorz。詳しくはこちらを参照して下さい(2/3追記)。

ちなみに私自身は正規表現よりもDOMパーサを用いた方が安全だからと思ってそれを選択したわけでもなんでもありません(*)。単に、自前で構文解析器を書くのが面倒だったからそうしたまでのことです(^^;

(*) というかあのややこしいHTML文書を正規表現で処理しようなんて発想自体思いつきませんでした。だから本家Sageのやり方を見た時は目からウロコだったんですけど……ねぇ。

但し少なくとも現在の高速HTMLフィルタは(その方式上)致命的な欠陥を含んでおり、今後も新たな脆弱性が発覚するリスクが極めて高いと判断しました。よって拙作の「Sage++ (Higmmer's Edition)」の次回リリースでは高速HTMLフィルタの機能を廃止させて頂く予定です(こちらの手元にはその変更を施したバージョンが既に存在してます)それまでの間、Sage++のユーザーの方は高速HTMLフィルタを決して使用しないように重ねてお願い致しますVersion 1.3.10にて高速HTMLフィルタモードを廃止しました。また本家Sage 1.3.9/1.4ユーザーの方もこちらのエントリを参考に各自で必要な対策を取って頂ければと思います(勿論、直ちに使用を中止するのもそのうちのひとつです)。

(追記) 本家Sageプロジェクトより公式版Sage 1.3.10がリリースされましたが、やはり、まだ修正されていない脆弱性が残っているようです。詳しくはこちらのエントリを参照して下さい。

*

尚、私はセキュリティの専門家でもなければWebアプリケーションやネットワーク等々に関する専門技術も何も持ち合わせていない単なる素人です。よって上記内容には多分に間違いを含んでいると思います。もしそれらに気づかれた方は遠慮なくビシバシご指摘頂けるとありがたいです。

トラックバック

この記事について書く(FC2ブログユーザー)
※言及リンクの無いトラックバックは無効です

PageTop▲

コメント

PageTop▲

コメントの投稿

 
 
 
 
 
 (後で編集・削除したいなら必須)
 
  

PageTop▲