以前の日記で、IE8β2のtoStaticHTML関数にバグがあると書きました。
そのバグについては、発見したときにMSに報告しました。その後、特に「直した」という連絡はありませんが、IE8の正式版では修正されていました。
β2にあったバグのPOCは、以下のようなものです。
<body> <div id="foo"></div> <script> var tmp="<img style=\"color:expression(alert('; width:x'))\">"; document.getElementById('foo').innerHTML = toStaticHTML(tmp); </script> </body>
ポイントは、alertのちょっとうしろに入れたセミコロンです。β2では、セミコロンが含まれていると、そこで1つの宣言が終わると解釈していました。かなり荒っぽい解釈です。
IE8正式版で上記のHTMLを表示すると、style属性の中身はざっくり空っぽにされます。つまり何らかの対策がされたということだと思います。
そのほかのパターンも試して見ましたが、CSSに関しては非常に大胆な処理がされます。具体的にいうと、括弧がCSSに含まれていると、それだけではじいてしまいます。
ですので、以下のHTMLはJavaScriptを含んでいないのにも関わらず、toStaticHTMLを通りません。
<b style="background-image: url('http://...');">111</b> <b style="font-family: 'foo(';">111</b>
括弧「(」は、「(」や「\0028」や「\28」にしても、とにかく通してくれません。他にも「\26#x28;」などいろいろな手を使ってみましたが、文字参照まわりは割とまじめに正規化するようになっているため、「(」を入れることができません。なお、括弧が駄目なのは、style属性だけでなくstyleタグも同じです。
攻撃を防ぐためにはこれでよいのでしょうけども、実用上の観点でいうと「(」が使えないのは困ることがあると思います。
その他にも気になることがいくつかあります。
■入力A <style>ul > li {color:red;}</style> ■出力A (HTMLエンコードされる) <style> ul > li {color:red;} </style> --------------------------------------------------- ■入力B <style>li {font-family:'xxx\27\7Dyyy';}</style> ■出力B (CSSの「\」エンコードがデコードされる) <style> li {font-family:'xxx'}yyy';} </style> --------------------------------------------------- ■入力C <style>li {font-family:'xxx\yyy';}</style> ■出力C (なぜかNULL文字が出てくる) <style> li {font-family:'xxx�yyy';} </style>
上のBはXSSまであと一歩という感じですが、結局括弧が入らないために、どうにもなりません。
ただし方向性を少し変えると、うまいことXSSできる場合があります。また、一定の条件がそろえば、XSSできるような軽微な問題も残っています(いずれも互換モードの場合のみ)。CSS周りは、たいていのXSSフィルタにとって最大の鬼門ですが、toStaticHTMLも例外ではないようです。