缓冲区溢出攻击
缓冲区溢出攻击 试图使 PHP 应用程序中(或者更精确地说,在 Apache 或底层操作系统中)的内存分配缓冲区发生溢出。请记住,您可能是使用 PHP 这样的高级语言来编写 Web 应用程序,但是最终还是要调用 C(在 Apache 的情况下)。与大多数低级语言一样,C 对于内存分配有严格的规则。
缓冲区溢出攻击向缓冲区发送大量数据,使部分数据溢出到相邻的内存缓冲区,从而破坏缓冲区或者重写逻辑。这样就能够造成拒绝服务、破坏数据或者在远程服务器上执行恶意代码。
防止缓冲区溢出攻击的惟一方法是检查所有用户输入的长度。例如,如果有一个表单元素要求输入用户的名字,那么在这个域上添加值为 40 的 maxlength
属性,并在后端使用 substr()
进行检查。清单 13 给出表单和 PHP 代码的简短示例。
清单 13. 检查用户输入的长度
<?
php
if
(
$_POST
[
'
submit
'
]
==
"
go
"
){
$name
=
substr
(
$_POST
[
'
name
'
]
,
0
,
40
);
//
continue processing....
}
?>
<
form action
=
"
<?php echo $_SERVER['PHP_SELF'];?>
"
method
=
"
post
"
>
<
p
><
label
for
=
"
name
"
>
Name
</
label
>
<
input type
=
"
text
"
name
=
"
name
"
id
=
"
name
"
size
=
"
20
"
maxlength
=
"
40
"
/></
p
>
<
p
><
input type
=
"
submit
"
name
=
"
submit
"
value
=
"
go
"
/></
p
>
</
form
>
为什么既提供 maxlength
属性,又在后端进行 substr()
检查?因为纵深防御总是好的。浏览器防止用户输入 PHP 或 MySQL 不能安全地处理的超长字符串(想像一下有人试图输入长达 1,000 个字符的名称),而后端 PHP 检查会确保没有人远程地或者在浏览器中操纵表单数据。
正如您看到的,这种方式与前一节中使用 strlen()
检查 GET
变量 pid
的长度相似。在这个示例中,忽略长度超过 5 位的任何输入值,但是也可以很容易地将值截短到适当的长度,如下所示:
清单 14. 改变输入的 GET 变量的长度
<?
php
$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. 防止十六进制字符串
<?
php
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
; }
?>
<
form action
=
"
<?php echo $_SERVER['PHP_SELF'];?>
"
method
=
"
post
"
>
<
p
><
label
for
=
"
name
"
>
Name
</
label
>
<
input type
=
"
text
"
name
=
"
name
"
id
=
"
name
"
size
=
"
20
"
maxlength
=
"
40
"
/></
p
>
<
p
><
input type
=
"
submit
"
name
=
"
submit
"
value
=
"
go
"
/></
p
>
</
form
>
您可能会发现这一系列操作有点儿太严格了。毕竟,十六进制串有合法的用途,比如输出外语中的字符。如何部署十六进制 regex
由您自己决定。比较好的策略是,只有在一行中包含过多十六进制串时,或者字符串的字符超过特定数量(比如 128 或 255)时,才删除十六进制串。