比較的新しい攻撃方法に、不完全なマルチバイト文字列を送信することでHTMLに 記述されているクォートを無効化する方法があります。この攻撃はHTML エス ケープのみでは防げない事に注意が必要です。では、どのように対策をすれば良 いのでしょうか?

まずは、不完全なマルチバイト文字を利用してクォート(")を無効化できるこ とを確認しましょう。次のスクリプトをブラウザから実行して下さい(最後の ダブルクォテーションとPHPタグの間にスペースを入れないで下さい)。

<?php
$str 
urldecode('%81');
header('Content-Type: text/html; charset=SJIS');
?>

<?php echo htmlentities($strENT_QUOTES'SJIS'?>"

コードが分かりづらいので注意してください。PHPタグを2つに大別したのは、 下側のコードを強調するためです。

このスクリプトを実行すると、コードの最後にあるダブルクォテーション(”) が画面から消えたと思います。これはシングルクォーテーション(’)の場合も 同様です。つまり、送られてきた文字列の最後が「%81」(Shift_JISの場合)で あれば、最後のクォートを無効化できてしまうのです。

これを使えば、次の様なスクリプトはXSS脆弱となります。

<input type='text' name='test' value="<?php echo
htmlentities($_GET['test'], ENT_QUOTES'SJIS'?>" />
<input type='text' name='hoge' value="<?php echo
htmlentities($_GET['hoge'], ENT_QUOTES'SJIS'?>" />

このページをtarget.phpとして保存を行い、以下のようなHTMLを通じてリンクを クリックしてみましょう。

<a
href="target.php?test=test%81&hoge=%20onMouseover=alert('XSS')%20%81">XSSテ
スト</a>

INPUTタグは1つしか現れず、マウスを上にのせるとアラートでXSSと表示された と思います。その原因は、testとhogeの値の最後にある「% 81」により各々の value属性値を囲んでいる後側のクォートが消されることです。出力は次のよう になります。

<input type='text' name='test' value="test />
<input type='text' name='hoge' value=" onMouseover=alert(&#039;XSS&#039;) />

最初のINPUTタグのvalue属性値は直後のクォートから、2つ目のINPUTタグの valueの直後にあるクォートまでとなります(test~'hoge' value=)。この結 果、onMouseoverが有効になります。

これを防ぐ方法は対象となる文字列の文字コードを同じ文字コードへ変換するこ とです。これにより、有効ではない文字を取り除いて下さい。

$str = mb_convert_encoding($str, 'SJIS', 'SJIS'); // SJISの場合

今のところXSS脆弱となるコードは限られていますが油断は禁物です。XSS脆弱で はない場合でもタグは確実に破壊されますし、他の攻撃方法に発展する可能性は 否定できません。



(アシアル 井川数志)


この記事は、アシアルが運営するPHP開発者のためのポータル&コミュニティサイト「PHPプロ!」で毎週配信しているPHP・TIPSメーリングリストを再録したものです。
同サイトでは、他にもPHP最新ニュースや、困ったときのQ&A掲示板、初心者向けのPHP講座など、PHP開発者をサポートする情報を掲載しています。