由于程序没有过滤$变量,所以攻击者可以用分号来追加执行命令。
攻击者输入如下请求可以执行命令:
?=;
PHP的命令执行函数还有(),(),()和``等。命令执行函数非常危险,慎用。如果要使用一定要严格检查用户输入。
解决方法:
要求程序员使用escapeshellcmd()函数过滤用户输入的shell命令。
启用safe_mode可以杜绝很多执行命令的问题,不过要注意PHP的版本一定要是最新的,小于PHP-4.2.2的都可能绕过safe_mode的限制去执行命令。
7、sql_inject
如下的SQL语句如果未对变量进行处理就会存在问题:
select*fromloginwhereuser='$user'andpass='$pass'
攻击者可以用户名和口令都输入1'or1='1绕过验证。
不过幸亏PHP有一个默认的选项magic_quotes_gpc=On,该选项使得从GET,POST,COOKIE来的变量自动加了addslashes()操作。上面SQL语句变成了:
select*fromloginwhereuser='1\'or1=\'1'andpass='1\'or1=\'1'
从而避免了此类sql_inject攻击。
对于数字类型的字段,很多程序员会这样写:
select*fromtestwhereid=$id
由于变量没有用单引号扩起来,就会造成sql_inject攻击。幸亏MySQL功能简单,没有sqlserver等数据库有执行命令的SQL语句,而且PHP的mysql_query()函数也只允许执行一条SQL语句,所以用分号隔开多条SQL语句的攻击也不能奏效。但是攻击者起码还可以让查询语句出错,泄漏系统的一些信息,或者一些意想不到的情况。
解决方法:
要求程序员对所有用户提交的要放到SQL语句的变量进行过滤。
即使是数字类型的字段,变量也要用单引号扩起来,MySQL自己会把字串处理成数字。
在MySQL里不要给PHP程序高级别权限的用户,只允许对自己的库进行操作,这也避免了程序出现问题被SELECTINTOOUTFILE...这种攻击。
8、警告及错误信息
PHP默认显示所有的警告及错误信息:
error_reporting=E_ALL~E_NOTICE
display_errors=On
在平时开发调试时这非常有用,可以根据警告信息马上找到程序错误所在。
正式应用时,警告及错误信息让用户不知所措,而且给攻击者泄漏了脚本所在的物理路径,为攻击者的进一步攻击提供了有利的信息。而且由于自己没有访问到错误的地方,反而不能及时修改程序的错误。所以把PHP的所有警告及错误信息记录到一个日志文件是非常明智的,即不给攻击者泄漏物理路径,又能让自己知道程序错误所在。
修改php.ini中关于Errorhandlingandlogging部分内容:
error_reporting=E_ALL
display_errors=Off
log_errors=On
error_log=/usr/local/apache/logs/php_error.log
然后重启apache,注意文件/usr/local/apache/logs/php_error.log必需可以让nobody用户可写。
9、disable_functions
如果觉得有些函数还有威胁,可以设置php.ini里的disable_functions(这个选项不能在httpd.conf里设置),比如:
disable_functions=phpinfo,get_cfg_var
可以指定多个函数,用逗号分开。重启apache后,phpinfo,get_cfg_var函数都被禁止了。建议关闭函数phpinfo,get_cfg_var,这两个函数容易泄漏服务器信息,而且没有实际用处。
10、disable_classes
这个选项是从PHP-4.3.2开始才有的,它可以禁用某些类,如果有多个用逗号分隔类名。disable_classes也不能在httpd.conf里设置,只能在php.ini配置文件里修改。
11、open_basedir
前面分析例程的时候也多次提到用open_basedir对脚本操作路径进行限制,这里再介绍一下它的特性。用open_basedir指定的限制实际上是前缀,不是目录名。也就是说"open_basedir=/dir/incl"也会允许访问"/dir/include"和"/dir/incls",如果它们存在的话。如果要将访问限制在仅为指定的目录,用斜线结束路径名。例如:"open_basedir=/dir/incl/"。
可以设置多个目录,在Windows中,用分号分隔目录。在任何其它系统中用冒号分隔目录。作为Apache模块时,父目录中的open_basedir路径自动被继承。