金利0無利息キャッシング – キャッシングできます

このブログが何かの役に立ちましたら → はてなポイントを送る / Amazon wishlist

2011-06-24

jQueryにおけるXSSを引き起こしやすい問題について

11:12 | jQueryにおけるXSSを引き起こしやすい問題について - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - jQueryにおけるXSSを引き起こしやすい問題について - 金利0無利息キャッシング – キャッシングできます

jQueryのマイナーバージョンアップがリリースされて取り敢えずバージョンアップしましょう、みたいになる状況を想定してたんだけど1ヶ月以上経ってしまったのでブログに書きます。twitterなどでちょくちょく書いていたので知っている人は知っていると思いますが、jQueryXSSを誘発しやすい問題があります。

これは簡潔に書くと、$() 関数CSSセレクタとして動作することを意図して書かれたコードが、タグ生成として機能する問題です。前提知識としてjQueryの$()関数には複数の機能があり、$("#id")はCSSセレクタとして機能し、$("<p>")はHTMLを生成します、また$(function(){...})はDOMReadyです。$()関数がタグを生成した際に、表示中のDOMツリーに挿入されていなくても、img src=dummy onerror=... といった形式で任意のJavaScriptコードを実行することが出来ます。

そのため

  • jQueryを使っていて
  • ユーザー入力値をバリデーションせずに $()関数に渡している

全てのWebサイトjQueryを使ったライブラリにはXSS脆弱性があります。ちなみに第一発見者は自分ではなくて@t_ashulaさんです。

修正方法

  • $() 関数にユーザー入力値を渡す場合にはバリデーションやフィルタする処理を入れる
  • jQuery側の挙動を変える https://gist.github.com/1038024
    • これは quickExprで定義されている正規表現を変更して、$() 関数に渡す文字列が /\s*</ で始まらない限り("<"で始まるか、空文字列の後に"<"で無い限り)タグ生成にならないようにする
    • 自分はこの変更を行った場合に、悪影響は殆ど無いと考えているが、どの程度の影響があるのかを正確に把握することができないので、jQueryのメインコミッターの方々の意見を知りたいと考えている

もちろん $() や $(element).html() でタグを意図的に生成している場合には、ユーザー入力値がそのまま出力されないか責任を持たないといけない。

典型的なXSSの事例

典型的には$(location.hash)がこれに該当します。なぜこういう実装が多いのかというと、location.hashは#で始まるため「idセレクタとして機能するだろう、もしユーザーが適当な値を入力しても指定したHTML要素が見つからなくてエラーが起きるだけだろう」と考えているためだと思われます。$("#tab_" + location.hash.substring(1) ) というようなケースもアウトです。

機能的な傾向で言うと、location.hash で「タブ切り替え」や「指定要素へのスクロール」「指定した要素の強調表示、デザイン変更」を実装しているコードの多くに問題があります。ブラウザXSSフィルタは検知しませんし、また、URLの#以降はサーバーサイドのアクセスログに残らないので悪用されたことを検知しづらいという問題もあります。

Google Code Searchなどを使って問題のある実装を多く見つけることが出来ます。自分が関わっているサービスや、いくつかの超有名なサイトでも同様のXSSがありました。

これはjQueryバグなのか?

バグではなく仕様、と見なすことも出来ますが、jQuery設計思想に起因する問題であることは確かです。$() が万能な関数であり、$(String) の挙動が文字列の内容によって変わるため、意図しない挙動が起こる可能性があります。jQueryを使ってるサイトが全て影響を受けるわけではありませんが、ユーザーがこの問題を知らなかった場合に、意図せずに危険なコードを書いてしまう可能性が非常に高いです。いくつかのライブラリ(jQuery pluginと呼ばれるもの)には明確にバグがあります。

自分が提案してるのは以下のようなことです

HTML生成よりもCSSセレクタを優先する
  • #で始まっていたら、常にid selectorとして機能すべきである
  • jQueryの$()はHTMLを生成するときに、タグで囲われていない部分は無視される(テキストノードを生成しない)
  • そのため、$("#<tag>") の挙動は $("<tag>")と等価で、本来必要のない文法であるからエラーにしてしまえば良い
  • 今まで動いていたコードが動かなくなる可能性が多少ある(大雑把にタグを含むテキストを渡してHTML Elementを生成してるケース)
    • 動いていたのがたまたまなので、jQuery側に非互換の修正を加えるほうがメリットが大きい
  • #だけを禁止した場合、#を取り除いた上でCSSセレクタの途中にユーザー入力を使っているケースでXSSが起こる(jQuery mobileのケース)
    • なので "<" で始まらない限りはタグ生成と見なさないほうが安全
  • XMLHttpRequestで受信したレスポンスからHTMLを組み立てるような動作をするときに、先頭にゴミがくっついているという可能性は多くあるだろう
    • なのでスペースやタブや改行は許容したほうが影響が少ないだろう
挙動を安全側に倒すpragma的なものを用意して、タグ生成自体を行わなくする
  • jQueryを使ったコードに問題が無いか精査する際に、全ての$()関数の使用箇所をチェックしないといけない
  • これはDOM Based XSSを発見するのを困難にしている
  • $() 関数HTMLを生成する機能が呼ばれた際にエラーを出すようにするようなpragmaを設けてしまえば、意図せずにタグが生成されるケースが無くなる
    • 代わりに $.html() などを作って置き換えてやる。html() をチェックすれば文字列からHTMLを生成している箇所がわかる。

あとで書く

なんか進展があったら適当に追記する

トラックバック - http://subtech.g.hatena.ne.jp/mala/20110624