PHP泛安全
PHP泛安全,PHP泛安全
*Author:xy7
*Data:2006/11/1
*---------------
*/
如今基于PHP的WEB程序是越来越安全了,php.ini默认时的 magic_quotes_gpc=on就在初始安全性上提高了一个档次.很多程序在接受到用户的输入时都会提前判断一下 get_magic_quotes_gpc() 即使这个开关没有开就马上addslashes()函数跟上进行转义,所以想在PHP程序中找到一个类似与以前ASP注入那样的漏洞是比较不容易的.
对于PHP的跨站其实也很好防范,用 htmlentities() 就可以了,不过当处理XML文件时采用这个函数可能会出现些问题,只需手工转换下那5个元字符就可以了。举个例子,以前写过篇文章关于XML文件隐患的,其实就是CDATA部件的问题,现在比较流行的说法是AJAX hacking,大概说下,看看以下代码:
if ($rssid == 0 OR $rssid == 7) {
} elseif (!empty($stmt)) {
$dbinfo =& $db-getResultSet($stmt, array('pageSize'=$pagesize));
if ($dbinfo === false) {
$msginfo = str_replace(']]', ']]', $lang['tpl.str0']);
$TPL_items .= <<<EOT
<item
<title{$msginfo}</title
<link{$fsetting['forumurl']}</link
<author{$fsetting['forumname']}</author
<pubDate{$datenow}</pubDate
<description<![CDATA[{$msginfo}]]</description
</item
EOT;
$msginfo是用户提交的,然后被程序写进RSS里以用来聚合,如果$msginfo的值为<sciriptalert('loveshell')</script时,RSS聚合解析后会原样输出,如果为]]<sciriptalert('loveshell')</script时,就可以跨站了,看看他的过滤:
if ($dbinfo === false) {
$msginfo = str_replace(']]', ']]', $lang['tpl.str0']);
很好意识到了这点,可是这句呢<title{$msginfo}</title,意识到问题所在很关键,重要的是要理解问题.
可是还是有很多会被程序员疏忽的地方,这是安全意识的问题,对于用户的任何输入,在写程序时脑子里都要提前做个思考:用户的输入是什么类型的?用户会有哪些输入方式?怎么处理用户的错误和非常规输入?
PHP的安全还体现在Safe Mode 和openbase-dir上.即使这样基于PHP底层的一些漏洞还是会直接影响到这两个非常重要的安全选项.举个例子:
比如error_log() Safe Mode Bypass
看他的语法:bool error_log ( string message [, int message_type [, string destination [, string extra_headers]]] )
输出错误信息到一个文件,可以这样写
<?
error_log("<? phpinfo();?", 3, "test.php");
?
运行在safe_mode关闭的情况下,直接访问test.php就可以看到phpinfo了,当safe_mode开的时候就会报错,再这样写:
<?
error_log("<? phpinfo();?", 3, "prefix://../../test.php");
?可以看到phpinfo又被执行了
看下漏洞代码:
PHPAPI int _php_error_log(int opt_err, char *message, char *opt, char *headers TSRMLS_DC)
{
php_stream *stream = NULL;
switch (opt_err) {
case 1: /*send an email */
{
#if HAVE_SENDMAIL
if (!php_mail(opt, "PHP error_log message", message, headers, NULL TSRMLS_CC)) {[next]
return FAILURE;
}
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Mail option not available!");
return FAILURE;
}
break;
case 2: /*send to an address */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "TCP/IP option not available!");
return FAILURE;
break;
case 3: /*save to a file */
stream = php_stream_open_wrapper(opt, "a", IGNORE_URL | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL);
if (!stream)
return FAILURE;
php_stream_write(stream, message, strlen(message));
php_stream_close(stream);
break;
default:
php_log_err(message TSRMLS_CC);
break;
}
return SUCCESS;
}
可以看到error_log函数的核心就是 php_stream_open_wrapper()函数,问题也就出在保存错误信息