UTF-8の動的コンテンツをShift_JISと誤認させることで成立するXSS

文字コード指定の無いUTF-8のコンテンツでは、ブラウザ側の文字コード自動認識でシフトJISと誤認させることで、HTMLの特殊文字を一切使わずにXSSが成立する場合があります。

原理

UTF-8 では1文字が3バイトマッピングされる場合があります。

そこで、これをShift_JISと誤認させると、3バイト目と次の1バイトをセットで1文字と誤認させることができます。これにより、HTMLの区切り文字の効力を失わせてスクリプトを注入することが可能です。

例:"あ" = E3 81 82 なので、シフトJISと誤認すると "縺" (E381) +余分な先行バイトの 0x82

具体例

例えば、動的なコンテンツ(UTF-8で応答を生成)

<html>
<head>
<title>abcde</title>
</head>
<body>
<form>
<input type=text value="ユーザー入力内容1"><input type=text value="ユーザー入力内容2">
</form>
</body>
</html>

ここで、
ユーザー入力内容1:"あ"
ユーザー入力内容2:" onmouseover=alert(1)//あ"

とすると、これは以下のように出力されます。

<html>
<head>
<title>abcde</title>
</head>
<body>
<form>
<input type=text value="あ"><input type=text value=" onmouseover=alert(1)//あ">
</form>
</body>
</html>

しかし、文字コード自動認識でShift_JISとして判定されると余ったUTF-8の3バイト目が次の文字と合わさって不明な文字(Windowsだと通常「・」扱い)として処理されるので

<html>
<head>
<title>abcde</title>
</head>
<body>
<form>
<input type=text value="縺・><input type=text value=" onmouseover=alert(1)//縺・>
</form>
</body>
</html>

となり、XSSが成立します。
応用パターンはきっと葉っぱ(id:hasegawayosuke)さんが見つけてくれるはず

結論

文字コードを明示しないコンテンツには予期せぬ脆弱性が潜んでいます。
「全部エスケープすれば大丈夫」とか 「xxxx でエンコードしているから大丈夫」というのは間違いです。
「危険な文字をエスケープしているから」とか「xxxx でエンコードしているから」という理由で正しいエンコーディング名を明示しないことは、この例のようなXSSを生む要因となります。

訂正

結論が説明不足だったので少し書き直しました。