Opera 9.27 (8841) におけるinnerHTML取得時の引用符解釈の不備

11/14追記

以下の件はHTMLページで発現する現象です。XHTMLでは別挙動です。気がつくのが遅れてしまいました。XHTMLでは、別の興味深い現象がありますので、まとめきれるようでしたら日記に書くかもしれません。

11/18 追記

Firefox関連記事を書きました。
http://d.hatena.ne.jp/hoshikuzu/20091117

前振り

Opera 9.27 (8841) 以前において、innerHTML取得時の引用符解釈に不備があります。この不備は、2008年06月12日にリリースされたメジャーバージョンアップバージョンである Opera 9.5 (10063) 以後には存在していないと思われます。

IE に、よく似た不備があったことを、id:hasegawayosukeさんによる記事、「教科書に載らないWebアプリケーションセキュリティ 第1回[これはひどい]IEの引用符の解釈 - @IT」に手際よくまとめられています。以下、同記事のパクリとなりますけれど、Opera9.27 以前における不備について簡単ながら書いてみます。

問題点

さて、Opera9.27における引用符の扱いがセキュリティに及ぼす影響ですが、典型的には次のようなコードの場合に問題が発生します。

<div id="div1">
<img src="http://www.hatena.ne.jp/images/medal-diary-3.gif"
alt="ここに攻撃者の指定した文字列が挿入可能"> …… (1)
</div>
<div id="div2">
</div>
<script>
document.getElementById( "div2" ).innerHTML =
document.getElementById( "div1" ).innerHTML; // …… (2)
</script>
Webアプリケーション側で生成したHTMLの一部を、JavaScriptのinnerHTMLを利用して複製しています。このとき、例えば攻撃者がalt属性の入力として「" onmouseover=alert(1);//」を投入するば、Webアプリケーション側は、引用符を文字参照エスケープして「&quot; onmouseover=alert(1);//」のような文字列を出力することになるでしょう。すると、div1のinnerHTMLを取得している部分(2)では属性値内の&quot;が誤って解釈され、文字参照がほどけしまい、バックスラッシュでエスケープされます。結果的に生成されるHTMLは
<div id="div1">
<img src="http://www.hatena.ne.jp/images/medal-diary-3.gif"
alt="&quot; onmouseover=alert(1);//">
</div>
<div id="div2">
<img src="http://www.hatena.ne.jp/images/medal-diary-3.gif"
alt="\" onmouseover=alert(1);//">
</div>
のようになり、div2の中にあるimg要素ではalt属性が無意味な値に、onmouseoverイベントハンドラが有効に、なりますので、攻撃者によるXSSが成立してしまいます。
誤解していただきたくないのは、「攻撃者のコードを含むinnerHTMLのコピーはHTMLの出力に相当する。エスケープしてコピーしていないのが悪い」というのではない点です。
HTML生成時に必要なエスケープは(1)の部分を生成する時点で完了しており、innerHTMLのコピーではコピー元とコピー先のDOM構造は一致するべきで、この例のようにDOM構造がコピー前後で異なってしまうというのは、理解しがたい挙動なのです。

ブラウザサイドでの解決方法

もちろん、最新のOperaを使うことですね。;-P

サーバサイドでの解決方法

innerHTML内に攻撃者の文字列を埋め込むことができる段階で既に考え方として負けかもしれません。この件に限らず、そうしたことを行ってはいけません。

余談

私は以上を、手元のOpera 9.02 (Build 8585) で昨日みつけました。(正確には、かつてみつけていて当日記にも書いていたにも関わらず忘却していました)arc.opera.com や、ftp.opera.com から旧版の各Operaをダウンロードしてはインストールし、を繰り返しました。どの版で直っているのか知りたかったからです。調査が甘い上、時間がないこともあって、このとても不思議な不具合が修正されたという記事を、Googleなどでみつけられませんでした。opera.com や secunia.com なども丹念にみたつもりなのですが。もしも参考となる記事のポインタなどがありましたら、ご教示くださいませ。

さらに余談

Opera9.02以後のバージョンを自宅のパソコンにインストールしていません。プライベートでOperaを使う気がしませんでした。 この他に、JavaScriptの var で宣言できる変数の名前として、引用符が使えたりと、すごく甘い印象を受けたからです。 (このあたり、JavaScriptでのエスケープはどうあるべきか論考した際に気がつき、当日記でサンプルコードを記述しました。)変数の名前に引用符が使える件も修正されているようですから、そろそろ、Opera嫌いをやめるかもしれません。