本当は怖い文字コードの話
第5回 不正なバイト列の埋め込み
今回は,「不正なバイト列の埋め込み」という攻撃方法について紹介します。
文字列を入力とするソフトウェアにはさまざまなものがありますが,それらの処理系によっては,入力として与えた文字列中に,その文字コード上は不正となるようなバイト列を埋め込んでいたときに,それらのバイト列が無視されたり,想定外の文字に変換されてしまうことがあります。
たとえば,とあるソフトウェアにて
- (1) 処理A = 文字列中に特定の文字(あるいは文字列)が含まれていないか検査
- (2) 処理B = 処理Aから受け取ったデータを処理。その際に不正なバイト列が無視あるいは別の文字に変換される
という流れになっていた場合,後続の処理にて本来はフィルタリングされるべき文字列が含まれてしまうことになります。
このような流れを引き起こす具体的な例をいくつか紹介します。
Mozilla Firefoxにおける0x80の無視
Mozilla Firefox 2.0.0.12より前のバージョンでは,HTMLがShift_JISで書かれていた場合に0x80という,単体では意味を持たない(Shift_JISとしては不正な)バイト値が無視されるという問題がありました(他の符号化方式においても類似の問題があることが後に確認されましたが,現在のバージョンでは全て修正済みです)。
たとえば,Shift_JISで書かれた次のようなHTMLをFirefoxで開いた場合に,スクリプトが実行されていました。
<div [0x80]onmouseover="alert('xss');">aaa</div>
<[0x80][0x80]s[0x80][0x80]c[0x80]r[0x80]i[0x80]p[0x80]t[0x80]>
[0x80]document.write('[0x80]xss');</script>
Webメールのように部分的にHTMLを許容するようなWebアプリケーションにおいて,Webアプリケーション側がXSSを防ぐために,「<script>」あるいは「onmouseover」のような文字列をブラックリスト方式で検出していた場合,これらの文字列は検査対象と一致しないため,フィルタリングから除外され,結果としてXSS攻撃が成立していました。
Internet Explorerにおける0x00の無視
Internet Explorerにおいては,HTML中の0x00というバイト値が無視されるということが広く知られています。この動作は,Ineternet Explorer 6,7,8の各バージョンで共通していますので,おそらくMicrosoftとしても仕様上の動作という認識なのだと思います。
たとえば,次のようなHTMLファイルをInternet Explorerで開いた場合,スクリプトが実行されます。
<div [0x00]onmouseover="alert('xss');">aaa</div>
<[0x00][0x00]s[0x00][0x00]c[0x00]r[0x00]i[0x00]p[0x00]t[0x00]>
[0x00]document.write('[0x00]xss');</script>
前述の Firefox における0x80の無視と同じく,Webアプリケーション側がXSSを防ぐために,「<script>」あるいは「onmouseover」のような文字列をブラックリスト方式で検出していた場合,これらの文字列は検査対象と一致しないため,フィルタリングから除外され,結果としてXSS攻撃が成立していました。
Firefoxの例と非常によく似ていますが,Internet Explorerの場合はHTMLを記述している文字コードの符号化方式に依存しないという点で,影響を受ける範囲はより広いといえます。
対策
Firefox,IEの2例を示しましたが,たとえば「<script>」「onmouseover」のような,XSSのトリガとなるような特定の文字列(とブラウザが同義に解釈する文字列)をWebアプリケーション側で漏れなく検出することがいかに困難であるか,おわかり頂けたと思います。
もちろん,上の例以外にも「不正なバイト列の埋め込み」に限らずさまざまな攻撃パターンが存在しており,また日々新しい攻撃パターンが考えられています。Webアプリケーション側でこれらの攻撃パターン全てを把握して検出しようとすれば,すぐに破綻するのは目に見えています。
そのため,Webアプリケーション側では,「危険な文字列を検出」ではなく「安全な文字列を生成」するようにしなければなりません。たとえば,特定のHTMLの要素を許可しつつXSSを避けるようなWebアプリケーションでは,「<script>」「onmouseover」といった文字列を検出するのではなく,
- 出力しても安全な要素として <div> <a> <p> などを定めておく
- <a> の属性としては href,titleのみを許可する
- href の属性値としてhttp://およびhttps://で始まる文字列のみ許可する
のように,それぞれの部位で安全であることが明確な文字列のみを出力するようにしましょう。このような実装とすることで,「不正なバイト列の埋め込み」による文字列の検出漏れだけでなく,その他の方法による検出の回避を防ぐことができます。


