缓冲区溢出攻击
缓冲区溢出攻击 试图使 PHP 应用程序中(或者更精确地说,在 Apache 或底层操作系统中)的内存分配缓冲区发生溢出。请记住,您可能是使用 PHP 这样的高级语言来编写 Web 应用程序,但是最终还是要调用 C(在 Apache 的情况下)。与大多数低级语言一样,C 对于内存分配有严格的规则。
缓冲区溢出攻击向缓冲区发送大量数据,使部分数据溢出到相邻的内存缓冲区,从而破坏缓冲区或者重写逻辑。这样就能够造成拒绝服务、破坏数据或者在远程服务器上执行恶意代码。
防止缓冲区溢出攻击的惟一方法是检查所有用户输入的长度。例如,如果有一个表单元素要求输入用户的名字,那么在这个域上添加值为 40 的 maxlength 属性,并在后端使用 substr() 进行检查。清单 13 给出表单和 PHP 代码的简短示例。
清单 13. 检查用户输入的长度
if ($_POST[’submit’] == “go”){
$name = substr($_POST[’name’],0,40);
}
?>
” method=”post”>
Name
为 什么既提供 maxlength 属性,又在后端进行 substr() 检查?因为纵深防御总是好的。浏览器防止用户输入 PHP 或 MySQL 不能安全地处理的超长字符串(想像一下有人试图输入长达 1,000 个字符的名称),而后端 PHP 检查会确保没有人远程地或者在浏览器中操纵表单数据。
正如您看到的,这种方式与前一节中使用 strlen() 检查 GET 变量 pid 的长度相似。在这个示例中,忽略长度超过 5 位的任何输入值,但是也可以很容易地将值截短到适当的长度,如下所示:
清单 14. 改变输入的 GET 变量的长度
$pid = $_GET[’pid’];
if (strlen($pid)){
if (!ereg(”^[0-9]+$”,$pid)){
//if non numeric $pid, send them back to home page
}
}else{
//empty $pid, so send them back to the home page
}
//we have a numeric pid, but it may be too long, so let’s check
if (strlen($pid)>5){
$pid = substr($pid,0,5);
}
//we create an object of a fictional class Page, which is now
//even more protected from evil user input
$obj = new Page;
$content = $obj->fetchPage($pid);
//and now we have a bunch of PHP that displays the page
?>
注 意,缓冲区溢出攻击并不限于长的数字串或字母串。也可能会看到长的十六进制字符串(往往看起来像 \xA3 或 \xFF)。记住,任何缓冲区溢出攻击的目的都是淹没特定的缓冲区,并将恶意代码或指令放到下一个缓冲区中,从而破坏数据或执行恶意代码。对付十六进制缓 冲区溢出最简单的方法也是不允许输入超过特定的长度。
如果您处理的是允许在数据库中输入较长条目的表单文本区,那么无法在客户端轻松地限制数据的长度。在数据到达 PHP 之后,可以使用正则表达式清除任何像十六进制的字符串。
清单 15. 防止十六进制字符串
if ($_POST[’submit’] == “go”){
$name = substr($_POST[’name’],0,40);
//clean out any potential hexadecimal characters
$name = cleanHex($name);
//continue processing….
}
function cleanHex($input){
$clean = preg_replace(”![\][xX]([A-Fa-f0-9]{1,3})!”, “”,$input);
return $clean;
}
?>
” method=”post”>
Name
您 可能会发现这一系列操作有点儿太严格了。毕竟,十六进制串有合法的用途,比如输出外语中的字符。如何部署十六进制 regex 由您自己决定。比较好的策略是,只有在一行中包含过多十六进制串时,或者字符串的字符超过特定数量(比如 128 或 255)时,才删除十六进制串。