PHP 遵从大多数服务器系统中关于文件和目录权限的安全机制。这就使管理员可以控制哪些文件在文件系统内是可读的。必须特别注意的是全局的可读文件,并确保每一个有权限的用户对这些文件的读取动作都是安全的。
PHP 被设计为以用户级别来访问文件系统,所以完全有可能通过编写一段 PHP 代码来读取系统文件如 /etc/passwd,更改网络连接以及发送大量打印任务等等。因此必须确保 PHP 代码读取和写入的是合适的文件。
请看下面的代码,用户想要删除自己主目录中的一个文件。假设此情形是通过 web 界面来管理文件系统,因此 Apache 用户有权删除用户目录下的文件。
Example#1 不对变量进行安全检查会导致……
<?php
// 从用户目录中删除指定的文件
$username = $_POST [ 'user_submitted_name' ];
$userfile = $_POST [ 'user_submitted_filename' ];
$homedir = "/home/$username" ;
unlink ( "$homedir/$userfile" );
echo "The file has been deleted!" ;
?> Example#2 ……文件系统攻击
<?php
// 删除硬盘中任何 PHP 有访问权限的文件。如果 PHP 有 root 权限:
$username = $_POST [ 'user_submitted_name' ]; // "../etc"
$userfile = $_POST [ 'user_submitted_filename' ]; // "passwd"
$homedir = "/home/$username" ; // "/home/../etc"
unlink ( "$homedir/$userfile" ); // "/home/../etc/passwd"
echo "The file has been deleted!" ;
?> Example#3 更安全的文件名检查
<?php
// 删除硬盘中 PHP 有权访问的文件
$username = $_SERVER [ 'REMOTE_USER' ]; // 使用认证机制
$userfile = basename ( $_POST [ 'user_submitted_filename' ]);
$homedir = "/home/$username" ;
$filepath = "$homedir/$userfile" ;
if ( file_exists ( $filepath ) && unlink ( $filepath )) {
$logstring = "Deleted $filepath\n" ;
} else {
$logstring = "Failed to delete $filepath\n" ;
}
$fp = fopen ( "/home/logging/filedelete.log" , "a" );
fwrite ( $fp , $logstring );
fclose ( $fp );
echo htmlentities ( $logstring , ENT_QUOTES );
?> Example#4 更安全的文件名检查
<?php
$username = $_SERVER [ 'REMOTE_USER' ]; // 使用认证机制
$userfile = $_POST [ 'user_submitted_filename' ];
$homedir = "/home/$username" ;
$filepath = "$homedir/$userfile" ;
if (! ctype_alnum ( $username ) || ! preg_match ( '/^(?:[a-z0-9_-]|\.(?!\.))+$/iD' , $userfile )) {
die( "Bad username/filename" );
}
//后略……
?> 根据操作系统的不同,存在着各种各样需要注意的文件,包括联系到系统的设备(/dev/ 或者 COM1)、配置文件(/ect/ 文件和 .ini文件)、常用的存储区域(/home/ 或者 My Documents)等等。由于此原因,建立一个策略禁止所有权限而只开放明确允许的通常更容易些。
由于 PHP 的文件系统操作是基于 C 语言的函数的,所以它可能会以您意想不到的方式处理 Null 字符。 Null字符在 C 语言中用于标识字符串结束,一个完整的字符串是从其开头到遇见 Null 字符为止。 以下代码演示了类似的攻击:
Example#5 会被 Null 字符问题攻击的代码
<?php
$file = $_GET [ 'file' ]; // "../../etc/passwd\0"
if ( file_exists ( '/home/wwwrun/' . $file . '.php' )) {
// file_exists will return true as the file /home/wwwrun/../../etc/passwd exists
include '/home/wwwrun/' . $file . '.php' ;
// the file /etc/passwd will be included
}
?> 因此,任何用于操作文件系统的字符串(译注:特别是程序外部输入的字符串)都必须经过适当的检查。以下是上述例子的改进版本:
Example#6 验证输入的正确做法
<?php
$file = $_GET [ 'file' ];
// 对字符串进行白名单检查
switch ( $file ) {
case 'main' :
case 'foo' :
case 'bar' :
include '/home/wwwrun/include/' . $file . '.php' ;
break;
default:
include '/home/wwwrun/include/main.php' ;
}
?> © 2005-2008 BlaBla.cn 版权所有