実はこの話題についてずっと前から話したいと思っていたのですが、国内の PHP サイトの多くに XSS の脆弱性があることが分かりました。今日は偶然PHP5のXSS脆弱性を発見したので、ここでまとめておきたいと思います。ちなみに、PHP5を使用している友人はパッチを適用するかアップグレードするのが最善です。
XSS が何なのかわからない場合は、ここまたはここを読んでください (中国語版の方がわかりやすいかもしれません)。
多くの国内フォーラムにはクロスサイト スクリプティングの脆弱性があります。たとえば、Discuz 4.0.0RC3 をターゲットとした Google Hack XSS 攻撃の例を次に示します。海外ではこのような例が多く、Googleでも出てきましたが、12月上旬に修正されました。クロスサイト攻撃は簡単に構築できますが、非常に巧妙で検出が困難です (通常は情報を盗み、すぐに元のページに戻ります)。
ここでは攻撃方法については説明しません (質問しないでください)。主に攻撃を防ぐ方法について説明します。まず、クロスサイト スクリプティング攻撃はユーザー入力の厳密なフィルタリングが欠如していることが原因で発生するため、すべてのデータが Web サイトやデータベースに入力される前に、起こり得る危険を阻止する必要があります。一重引用符や二重引用符を含む不正な HTML コードの場合は、htmlentities() を使用できます。
$str = "A 'quote' is bold";
// 出力: A 'quote' is < b>bold
echo htmlentities($str);
// 出力: 'quote' は bold ENT_QUOTES);
?>
これにより、不正なスクリプトが無効になります。
ただし、htmlentities() のデフォルトのエンコードは ISO-8859-1 であることに注意してください。不正なスクリプトが他の形式でエンコードされている場合、フィルタリングされない可能性がありますが、ブラウザはそれを認識して実行できます。この問題について話す前に、まずこの問題をテストするためのサイトをいくつか見つけます。
这里提供一过滤非法脚本的関数:
function RemoveXSS($val) {
// すべての印刷不可能な文字を削除します。 CR(0a)、LF(0b)、TAB(9) は許可されています
//これにより、
// n、r、t による分割は、一部の入力では*許可されている*ため、後で処理する必要があることに注意してください。
$val = preg_replace('/([x00-x08][x0b-x0c] [x0e-x20])/', '', $val);
// ストレートな置換。これらは通常の文字であるため、ユーザーはこれらを必要とする必要はありません。
// これにより、
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:? /={}[]-_|'\';
for ($i = 0; $i < strlen($search); $i ) {
// ;? ; に一致します。これはオプションです。
//0{0,7} は、任意の埋め込まれたゼロに一致します。これは、8 文字までです。
// x0040 @ 16 進値を検索
$val = preg_replace('/([x|X]0{0,8}'.dechex(ord($search[$i])).';?) /i', $search[$i], $val); // with a ;
// @ @ 0{0,7} は '0' に 0 ~ 7 回一致します
$val = preg_replace ('/({0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // a ; }
// 現在、残っている空白攻撃は t、n、r のみです
$ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', ' xml」、「blink」、「link」、「style」、「script」、「embed」、「object」、「iframe」、「frame」、「frameset」、「ilayer」、「layer」、「bgsound」 、「タイトル」、「ベース」);
$ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint '、'onbeforeunload'、'onbeforeupdate'、'onblur'、'onbounce'、'oncellchange'、'onchange'、'onclick'、'oncontextmenu'、'oncontrolselect'、'oncopy'、'oncut'、'ondataavailable'、 'ondatasetchanged'、'ondatasetcomplete'、'ondblclick'、'ondeactivate'、'ondrag'、'ondragend'、'ondragenter'、'ondragleave'、'ondragover'、'ondragstart'、'ondrop'、'onerror'、'onerrorupdate ','onfilterchange'、'onfinish'、'onfocus'、'onfocusin'、'onfocusout'、'onhelp'、'onkeydown'、'onkeypress'、'onkeyup'、'onlayoutcomplete'、'onload'、'onlosecapture'、 'onmousedown'、'onmouseenter'、'onmouseleave'、'onmousemove'、'onmouseout'、'onmouseover'、'onmouseup'、'onmousewheel'、'onmove'、'onmoveend'、'onmovestart'、'onpaste'、'onpropertychange ', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart'、'onstart'、'onstop'、'onsubmit'、'onunload');
$ra = array_merge($ra1, $ra2);
$found = true; //前のラウンドで何かが置き換えられている限り、置き換えを続けます
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i ) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j ) {
if ($j > 0) {
$pattern .= '(' ;
$pattern .= '([x|X]0{0,8}([9][a][b]);?)?';
$pattern .= '|( {0,8}([9][10][13]);?)?';
$pattern .= ')?';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'
$val = preg_replace($pattern, $replacement, $val); // 16 進数のタグをフィルタリングします
if ($val_before == $val) {
// 置換は行われなかったので、ループを終了します
$found = false;
}
}
}
}