jQuery Migrate Plugin なんてなかった件

いくつかのサイトで実際に遭遇してますが,jQueyr 1.6.3 で解決( jQuery Bug #9521 )したはずの $("a[href=" + hash + "]") タイプの XSS が ( 2013/11/18 現在 1.2.1 を含む全ての) jQuery Migrate Plugin で復活します.

色々追ってみて,やっぱりまだ動くけどと言ったら,8月にすでに,実際バグだけどこれで想定通りなんだと言われてた(He told me that this was not, in fact, a bug, but was working as intended.) のでそういうことだったみたいです.

jQuery 本体側の変遷

jQuery$() には複数の機能があり,引数の型や数によって動作が変わります.$(function(){}) で呼ばれると,DOM Ready へのイベントハンドラになり,$(String) で呼び出されると,CSS 風のセレクタとして要素の選択になったりHTMLタグっぽいと要素生成になったりします.

この $(String) でのセレクタと要素生成を判別しているのが,quickExpr という変数で,以下の様な変更がされてきました.

  • jQuery 1.6.2 quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
  • jQuery 1.6.3 quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
  • jQuery 1.9.0 rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,
  • jQuery 1.10.0 rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,

1.6.2 までは,# から始まる文字列を id セレクタ扱いにしているように見えて,前半部分は (<以外なんでも)<(だいたいなんでも)>(>以外なんでも) という文字列と,後半は # と英数字のみと一致するため,# などだいたいが前半の要素生成文字列とみなされ,id セレクタとみなされる条件がかなり弱くなっていました.

これを 1.6.3では jQuery Bug #9521 で,前半を (<と#以外なんでも)<(だいたいなんでも)>(>以外なんでも) に変えたため,# が含まれていたら id セレクタ(を含むセレクタ)とみなされて Sizzle 側でエラーとなるようになりました.

$(location.hash)$("a[href=" + location.hash + "]") には効果がありましたが,$("." + location.hash.substring(1)) というような,# を含まない文字列での要素生成の問題は残り,1.9.0 jQuery Bug #11290 でひとまず解消しました.

1.9.0 で要素生成は先頭が < の時に限られるようになりました.$(location.hash.substring(1)) などとしていれば当然要素生成になってしまいますが,CSS セレクタ として < が1文字目に出てくることはないので,実質的にこれでセレクタのつもりが不意な要素生成という問題は起こらなくなったはずでした. その後,1.10.0 では先頭の空白が許容されるようになりましたが,これは,クライアントサイドでのテンプレートが壊れる問題 への対応策として導入されたもので,セレクタとしての振る舞いに影響を与えないので特に問題はありません.

jQuery Migrate Plugin の変遷

1.9.0/2.0.0beta と同時に Migrate Plugin がリリースになりました.Migrate Plugin では$(String) の振る舞いもPlugin の対象領域になりました.

セレクタと要素生成の判別処理に関しては当初,1.6.2 と同程度にまで退行しました.

jQuery.com が退行した Migrate Plugin 1.1.0 と jQuery 1.9.0 とで構成されていたため,jQuery Migrate is a Sink, too?! で潜在的な脅威として取り上げられ,XSS · Issue #36 · jquery/jquery-migrate となり,1.2.0 に反映となりました.

その後,属性値内の # が XSS と誤認識される問題改行コードを入れられない問題先頭に < をという警告が出る問題 があり,1.2.1 が出ましたが,1.6.2 相当でも 1.6.3 相当でもない中途半端な現在に至ってます.

一応 jQuery Migrate Plugin 1.0.0 の頃から rquickExpr の上にコメントで // Note this does NOT include the # XSS fix from 1.7! と書いてあるので,互換性は維持されるが脆弱になることは考慮していたと思われますが,jQuery Migrate Plugin 1.2.1 でこれが削除され,// Note: XSS check is done below after string is trimmed に書き換わりましたが本質的には何も変わってません