正如数百个其他答案/评论所指出的那样,每个人现在都应该使用mysqli
。除此之外, mysql_real_escape_string()
顾名思义,用于转义将在 MySQL 查询中使用的字符串。它没有其他用途。它并不能保护您免受 XSS 攻击,我将演示...
设置:
// represent a string without using quotes
function ab($str) {
$out = array();
for ($i = 0; $i < strlen($str); $i++) {
($chr = _ab($str[$i])) || ($chr = 'String.fromCharCode('.ord($str[$i]).')');
}
return implode('+', $out);
}
// represent a character without quotes
function _ab($chr) {
$alpha = array(
'Array', 'Boolean', 'Date', 'Function', 'Iterator', 'Number', 'Object',
'RegExp', 'String', 'ArrayBuffer', 'Float32Array', 'Float64Array', 'JSON',
'Int16Array', 'Int32Array', 'Int8Array', 'Uint16Array', 'Uint32Array',
'Uint8Array', 'Uint8ClampedArray', 'Error', 'EvalError', 'InternalError',
'RangeError', 'ReferenceError', 'StopIteration', 'SyntaxError', 'parseInt',
'TypeError', 'URIError', 'decodeURI', 'decodeURIComponent', 'encodeURI',
'encodeURIComponent', 'eval', 'isFinite', 'isNaN', 'parseFloat', 'uneval'
);
// sort function names by length to minimize output
usort($alpha, function($a, $b){return strlen($a) - strlen($b);});
foreach ($alpha as $fn) {
if (($i = strpos($fn, $chr)) !== false) return "$fn.name[$i]";
}
return false;
}
$mb = chr(0xC2).chr(0x8F); // eats backslashes for breakfast
这个怎么运作:
$msg = ab('exploited!');
$payload = "console.log($msg);";
$uri = "$mb\" onmouseover=$payload title=XSS";
// try to sanitize URI
$uri = mysql_real_escape_string($uri);
?>
<a href="<?php echo $uri ?>">click me</a>
这会生成“URI”,例如:
" onmouseover=console.log(Date.name[2]+eval.name[0]+isNaN.name[1]+Date.name[2]); title=hi
它直接通过mysql_real_escape_string()
(因为第一个“字符”实际上是一个部分多字节字符0xC28F
,它立即吃掉它之后的字符 - 反斜杠mysql_real_escape_string()
插入以转义引号),产生:
<a href="\" onmouseover=THE_PAYLOAD title=XSS">click me</a>
浏览器解释为:
<a title="XSS"" onmouseover="THE_PAYLOAD" href="\">click me</a>
您可以在 Firefox 或 IE 中打开控制台自行测试,当您将鼠标悬停在链接上时,您会看到任意 JS 代码正在执行,尽管使用了mysql_real_escape_string()
.
mysql_real_escape_string()
也不能保护您免受javascript:
以下链接的影响:
javascript:$.post(%27/account/change-password%27,{newPassword:%27p0wned%27,newPassword2:%27p0wned%27});undefined;
由于 MySQL 中没有使用百分比编码,mysql_real_escape_string()
所以不知道%27
是单引号还是%22
双引号;所以它只是忽略它们。如果用户单击此链接,它将发送一个POST
请求,将其密码更改为攻击者选择的密码。它可以被修改为也重定向到用户期望去的页面,所以他们不知道刚刚发生了什么。
对当前设置进行此类攻击的唯一障碍是 URI 是相对于根的,因此它们总是以“/”开头,但正如第一个示例所示,多字节字符串可用于欺骗浏览器,即使结果标记被破坏,由于大多数浏览器的 HTML 解析器的宽大处理,该链接仍然有效。阅读您的问题后,我只花了 15 分钟就想出了这个例子。一个坚定的攻击者无疑会想出一些确实构成实际威胁的东西。
所以为了安全起见,你必须使用专门为 HTML 和 URL 设计的方法来清理你的 URI。OWASP 关于 URL 转义的指南是一个很好的起点。否则,这里的其他答案提供了一些更安全的选择。