swfobject.js がアレな話

 

もばいる全盛感のある世間的には今更,FLASH なんてどうでもいいし,swfobject.js 自体 2009年から更新されてないから,こんな古いものをと言われかねないような話ではあるものの,日本語での言及をあまり見てないし,つい先日もさる通信キャリアがトップページでやらかしてて多分知られてないんだろうなと思ったので書こうと思った次第.

swfobject.js とは何か

この記事の対象読者にとっては説明するまでもない話とはおもうけど一応前置きとして説明しておくと,swfobject.js は FLASH を web ページに埋め込むための JavaScript のライブラリ.クロスブラウザ対応してたり,面倒な HTML-tag のお作法を覚えなくても良くなったりとでデファクトスタンダードな感じのモノ.2007年とちょい古いがリクルートMTL の SWFObject v2.0 ドキュメント日本語訳や,AdobeSWFObject 2を用いたFlash Playerのバージョン検知とSWFファイルの埋め込みを読むと大体理解できるかと思う.

v2.x 系の前にv1.5 があってこっちもまだそれなりに使われてる.

何がマズイか

問題になる箇所は2ヶ所あるが,問題の根は同じ.

一つ目は,embedSWF() の flashVarsObj(,paraObj,attrObj) の扱い.もう一つは,ExpressInstall.

embedSWF()での flashVarsObj などの扱い

swfobject.js (v2.2) の embedSWF では flahvarsObj や parObj に設定されている値が HTML でエスケープされるべき値なのかどうかを関知しない.そのため,flashvarsObj に " を含む値を渡すと,Windows + IE 環境で,任意の HTML 要素挿入が可能になっている.SWFObject.js v2.2 でのサンプル

swfobject.js 1.5 でも同等の問題があり,addVariable() で " を含む値を渡すと全ブラウザで任意の要素が挿入できる.SWFObject.js v1.5 でのサンプル

ページの URL をFLASH に渡そうとして flashvars={'url':document.URL};swfobject.embedSWF(...,flashvars,...); としていたり,so.addVariable('referer', location.href ); としていたりして location.hash や location.search 経由で汚染されるのがよくあるパターン.これ自体は,swfobject のプロジェクトでも把握していて,FAQ の 9. How can I pass URIs or HTML code as a value using flashvars? に encodeURIComponent() を使ってエンコードしろと言っているように,ユーザ(ページ製作者)に一定の注意を促しては居る.ただし v2.x 系での話で v1.5 系で言及されてたかどうかはわからない.

HTML断片や URL を渡すからエンコードしなくちゃいけないのではなく,& と = が flashvars の特殊文字であり," が HTML の特殊文字であるために,それらを適切にエスケープしなければならないのだがこの FAQ のタイトルでは,そう言ってはないのでいつでも必要になる問題だということが見過ごされる可能性が高い.また,v2.2 において,flashvarsObj は paraObj.flashvars に変換されるだけの存在であり,実際に任意の HTML が挿入されるのは,paraObj から HTML 断片への変換処理にあることや,attrObj も同様に単に文字列連結しているだけで同様の危険性をはらんでいることからすると,flashvarsObj の扱いだけを注意すれば良いというものでもないこともこの FAQ から読み取るのは困難だと思う.

この話は,2008年にswfobject.js にチケット Issue 66: flashVars are not escaped (urlencoded) properly として登録され,長く議論されているが,swfobject.js 側が修正される可能性は殆ど無い.ライブラリ開発側としては,「そもそもswfobject.js を使わずとも flashvars 経由で URI を渡すならエンコードされてなければならないのだから先にエンコードすべき」「ライブラリ側で変換するようにすることですでにエンコードされている既存のコードに影響が出る」「encodeURIComponent が IE5.5+ を要求し,クロスブラウザ対応に制約が出る」と言う立場をとっている.限定的な場合に使える API があり,冒頭に挙げた Adobe 本家の資料でも最後に少しだけ言及しているが,後述するように余り役に立たない.

ExpressInstall

2つ目の問題は ExpressInstall 機能である.

swfobject.js の機能として FLASH の plugin はインストールされてるがバージョンが古い時に,その場でインストーラを呼び出してアップデートを促す ExpressInstall と呼ばれる機能がある.

これはいくつか細かい条件があるものの,要求するバージョンより古いバージョンがインストールされていると判定されると,内部で専用の FLASH の埋め込みコードを生成し,元の FLASH の代わりにインストーラを呼び出す FLASH を埋め込むことで実現している.この内部で生成している専用の FLASH の埋め込みコードに問題があり,一つ目の問題と同じ原因で v2.2 では IE で v1.5 ではほぼ全てのブラウザで 任意の HTML が挿入可能になっている.

v2.2 では,expressInstall.swf でインストールした後で戻ってくる URL (MMredirectUrl) を flashvars に設定するときに,URL (=window.location) を渡しているにもかかわらず,適切なエスケープをしていないために,location 経由の XSS を呼び込むことになっている.これ自体は問題として認識されている(Issue 172: ExpressInstall redirect not redirecting with all GET variable after updating Flash)が不完全なまま終了している.また,リダイレクトして戻ってくるページのタイトルを設定する MMdoctitle に document.title の先頭 47 文字をそのまま渡しているため,例えば検索結果の画面などで,ユーザ入力から TITLE 要素が作られる様なページで expressInstall が発動すると不具合が生じる可能性がある. SWFObject.js v2.2 での expressInstall のサンプルSWFObject.js v2.2 での expressInstall のサンプル2

v1.5 では,リダイレクトして戻ってくる URL が escape() してから設定されているため window.location を経由した XSS は起こらないが,v2.2 と同様に document.title を経由した問題が起きる可能性がある. SWFObject.js v1.5 での expressInstall のサンプル

対策

で,どうするかというと基本的にはエスケープをする以外にない.

一応,v2.x では,swobject.getQueryParamValue() を使うことで,埋め込んでいるページの document.location.search と document.location.hash で指定されているパラメータを「安全」に取り出せるようになっている.ただし取得元が document.location に限定されていて好き勝手な変数をエスケープできるようになっていない上,encodeURIComponent が無い環境ではいかなる変換もしないので,URL 全てを入れる場合や location 以外のところから取得したり,location を加工したりする場合,結局自力でやらなければならない.

  • SWFObject.js v1.5 はもうメンテされてないので使わない.使いたい場合は,addVariable や addParam に渡す値は適切にエスケープする.
  • SWFObject.js v2.2 を使う場合,
    1. flashvars を経由して値を渡す場合,& と = を flashvars のために," を IE での正常な HTML の生成のためにエスケープする.
    2. paraObj (PARAM 要素)や attObj (OBJECT要素の属性) 経由で渡す場合も " を IE での正常な HTML の生成のためにエスケープする.
    3. expressInstall を使わないように embedSWF の引数で xiSwfUrlStr に false になる値を設定する.
    4. expressInstall を使いたい場合,r427#331 の win.location を encodeURIComponent(win.location) か escape(win.location) かにする(参照. swfobject.jsの注意点(Google Code)). doc.title も同様エスケープする.どちらのエスケープもない環境はこの際切り捨てるか String.replace で頑張る.

SWFObject そのものは捨てて,jQuery SWFObject Plugin,(SWFObject | jQuery Plugins) を使うという方法もある.明示的にエスケープしないように指定されない限り内部でエスケープするように安全側に倒された実装になっている.

まとめ

SWFObject がエスケープされた値が渡されること期待した実装になっているのできちんとエスケープしないと XSS になるという話と,expressInstall が使われる場合にはエスケープされてない値で埋め込みコードが生成されるので,expressInstall が動作しないようにするか,パッチをあてて使うか,別のライブラリを使いましょうという話でした.

追記

一年たったので追記.

Github(https://github.com/swfobject/swfobject)にて2.3beta 版が公開中でまだ継続してく予定な様子.

上述の v2.2 までの問題はほぼ解消されている.が,かなりレアなケースで問題が起こすことが不可能ではないので doc.title もencodeURIComponent 掛けませんかとぷるり中.

追記2

プルリクエストがいつの間にか取り込まれてたのでちょっとだけ追記

この取り込まれた version で,ExpressInstall 時の MMdocTitle のエスケープが不十分なために MMredirectURL が上書きされてプラグインインストール後に任意のURL に飛ばされるというのが起こせなくなった.