比較的新しい攻撃方法に、不完全なマルチバイト文字列を送信することでHTMLに 記述されているクォートを無効化する方法があります。この攻撃はHTML エス ケープのみでは防げない事に注意が必要です。では、どのように対策をすれば良 いのでしょうか?
まずは、不完全なマルチバイト文字を利用してクォート(")を無効化できるこ とを確認しましょう。次のスクリプトをブラウザから実行して下さい(最後の ダブルクォテーションとPHPタグの間にスペースを入れないで下さい)。
<?php
$str = urldecode('%81');
header('Content-Type: text/html; charset=SJIS');
?>
<?php echo htmlentities($str, ENT_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('XSS') />
最初の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開発者をサポートする情報を掲載しています。