概要
JavaScript を介した XSS である DOM based XSS の Opera の UserJS による発見と対策について.
前提
ある程度の個人情報を登録しているユーザを抱えているサービスの開発者などが対象です.
DOM based XSS がなにかについては,OWASP DOM Based XSSを参照して下さい.
脅威
同一ドメインでユーザ情報の変更フォームがあると,第三者にその情報を奪取されます. 別ドメインでも,CORS が設定されてると抜かれる可能性があります. その他,ユーザの秘密情報に対する攻撃が行われます. 場合によっては,他のサービスに対するDDoS の踏み台としてユーザのリソースが消費されます.
発見
とりあえず,xss.js を UserJS として飼います.
見事 alert(2) が出れば任意の要素が突っ込まれる穴があるのは確定.
alert(4) だけが出れば,innerHTML 経由で突っ込まれるやっぱり穴があるのが確定.
alert(‘maybe’) が出たときは,微妙で,ページに元々 XMP 要素があっただけかもしれないし,ある程度対策されてて <xmp> は通ったけど他のは消された(ように見える)のかもしれないので駆除を検討します.
じゃあ何も alert が出なかったら問題ないのかと言うとそうではないが頭がいたい話なのです.
例えば ‘display:none’ が指定されている要素内で XSS が発動していて,img.onload が発生していないだけかもしれませんし,何かのイベントハンドラに脆弱性があるけどまだイベントが発火してなくてコードが実行されてないだけかもしれません.実際に,いわゆる「イイね」系のボタンにマウスオーバーすると alert が出るケースもあります.
なにはともあれ alert が出たらそのページには何か居るのはほぼ確定なので慌てず騒がず駆除の検討に移りましょう.
駆除
メンドイですが,Dragonfly なり Firebug なり 開発者ツールなりで DOM Tree の何処で刺さったかを確認して,何処のコードが原因かを追ってください.
原因となる箇所は location.href や document.URL を参照していて,document.write か innerHTML を使っている箇所なので,絞れるはずです.
原因となる箇所を見つけたら,修正します.
まず document.write や innerHTML を使わずに,document.createElement や elem.setAttribute で実装できるならそうするのがベターでしょう.だいたいの場合でブラウザが良しなに計らってくれます.
document.createElement 等での実装ができない場合,道は2つです.
外部から操作できない箇所で URL を保持しておけばよいので,ページ内に static に
var pageuri=‘http://example.com/foo/bar?q=123';
とするなどして location.href を使わないようにしておき,適切なエスケープを施した上で使うようにします.
これも不可能なばあい,# 以降 (location.hash) が必要かどうか,? 以降(location.search) が必要かどうかを検討して,必要なければ使わない ( location.protocol + location.host + location.pathname ) で済むようにして,必要に応じて encodeURIComponent でエンコードしてから使い,どうしても location.href を document.write や innerHTML で使いたいのなら escape 使ってください.
予防
発見や駆除の話は主に開発者向けなお話でユーザはどうすんのよと.
とりあえず,JS を無効にしておくというのが効果的なわけですが,現実的じゃないです.
ホワイトリスト方式で特定のサイトのドメインでだけ JS を有効にするというのもアリですが,そのサイトが読み込んでる外部の JS に穴がある場合があるので注意が必要です.信頼できる(している)サイトだからとホイホイリンクを踏んだらぐぬぬともなるわけです.
WebKit なブラウザを使うというのもひとつの手です.スクリプトが実行されたりや外部リソースへのアクセスが発生したりするような要素や属性の挿入を検知してなかった事にしてくれますし,特に Safari では,location.hash 内の ‘ を URL エンコード してくれるので,勝手な属性が生成されにくくなってます.が,やっぱり過信はダメで,innerHTML に代入されるタイプの XSS では反応しません.
IE9 でゾーンを厳しめにしておくというのもひとつの手です.
Firefox でも location.hash の ‘ が URL エンコードされるので一手間要りますし,NoScript などの addon で固めるというのもありでしょう.
で,Opera でどうするよとなるわけで,ユーザランドでの対処なので気休め程度にしかなりませんが,0.js を UserJS として入れてみるのはどうでしょう. decodeURI した location.href にタグを見つけると怪しいぞと言ってくれるので誤爆もそれなりにします.xss.js と同時に使うと,alert がウザイくらいでます.
結び
- ちゃんとエスケープしましょう.
- URL が必要なら rel=canonial を検討しましょう.