对于提供公共网络服务的ISP,基于安全性理由,建议apache和php都使用最小权限的公用设置.
针对特定用户所提出的涉及安全性能的特殊要求, 可以在不改变全局性的共用设置的情况下,
通过利用Apache Virtualhost的PHP扩展功能来实现. 只需在相应用户的Virtualhost的设置
段落中插入php_value,php_admin_value或php_admin_flag指令,就可以使该用户具有与全局
设置不同的权限和行为.
针对Apache的特定虚拟用户进行单独配置的相关语法如下:
php_admin_value name 1|0|string (value控制具体的参数)
php_admin_flag name on|off (flag控制on或off,适用于Apache2.20版本)
请注意: 上述针对虚拟用户的设置命令,可以直接设置在单元里面,或者设置在
相应用户的单元里面.
例如,要将ernest这个用户的register_globals功能打开,并且将upload_max_filesize调高到
5M,同时关闭safe_mode,但又不影响其他用户,就可以在该用户的VirtualHost里面加如下几行:
php_value upload_max_filesize 5M
php_value register_globals 1
php_value safe_mode 0
php_flag safe_mode Off
php_admin_value safe_mode 0 #(for Apache2.20)
php_admin_flag safe_mode Off #(for Apache2.20)
在php.ini配置文件中的大部分功能,均可以用这种方式来调整,调整后应重新启动apache,然后
就可以在phpinfo中看到中间栏的Local Value同右边栏位的Master Value是不同值.
请注意: 有些参数值的设定方法跟它们在php.ini配置文件中的设置方法可能不一样,例如
上面的”register_globals 1″, 原来在php.ini中应是”register_globals On”.
################################################################################
================================================================================
(1) safe_mode: 以安全模式运行PHP;
——————————————————————————–
在php.ini文件中使用如下选项(这是影响全局的设置):
safe_mode = On (使用安全模式)
safe_mode = Off (关闭安全模式)
PHP的安全模式是为了试图解决共享伺服器(shared-server)的安全问题而专门设立的. 然而
从结构上看, 试图在PHP层面上解决这个问题其实是不合理的, 只是考虑到修改WEB伺服器层
和操作系统层都显得非常的不现实, 因此许多使用者,特别是提供公共网络服务的ISP供应商,
大多都在其服务器中要求以安全模式来运行PHP,用以防止合法用户的跨站读取或越权操作等
危险行为, 以及将非授权用户的恶意行为所造成的影响降到最低范围.
参考网址: http://hk2.php.net/features.safe-mode
在Apache的httpd.conf中VirtualHost的相应设置方法(这是针对特定用户的设置):
php_admin_flag safe_mode On (使用安全模式)
php_admin_flag safe_mode Off (关闭安全模式)
或者:
php_admin_value safe_mode 1 (使用安全模式)
php_admin_value safe_mode 0 (关闭安全模式)
严重警告: 如果在全局性设置中已经启用了safe_mode的功能,但又在特别的用户虚拟空间中
关闭该用户的safe_mode的功能,这就等于给予了该用户特殊的权限,允许他不须受safe_mode
的限制而自由地使用系统的服务,也就是说所有原来被全局性的safe_mode功能所禁止的行为,
例如跨站读取或越权操作等都可以被该用户执行, 这就好像在原本安全设防的金库中打开了
一个可供该用户自由进出的洞,因此任何使用该用户空间的应用都不再被全局性的safe_mode
保护所限制,当然这就意味著整个系统的安全性都可能会受到该用户空间的影响,包括可能因
该空间应用的漏洞而导致整个系统被入侵等等. 所以,这样的针对特定用户的特殊设置,应当
作为一种特许授权的方式来考虑, 并有必要建立有效的监控机制以防止该用户滥用系统资源,
否则, 一旦该用户变得不再可信任或他的网站程序存在漏洞, 那么您的整体系统所受的影响
就会同完全没有启用safe_mode一模一样.
================================================================================
================================================================================
(2) safe_mode_include_dir: 无需UID/GID检查的目录
——————————————————————————–
当您按照前面(1)所述之设置启用PHP的安全模式之后,PHP的脚本在运行时就会对所有被操作
的目录以及文件进行针对UID/GID的匹配性检查: 即检查被操作目录或文件的UID或GID,是否
同当前PHP脚本文件的UID或GID一样.
然而, 如果您的系统允许用户的PHP脚本访问公共路径的话(例如很多较旧的Forum或Gallery
程序都会直接引用系统文件来扩展当时PHP还未能支持的功能), 那么这种设置就会造成麻烦.
而使用safe_mode_include_dir设置可以指定某些目录, 当PHP脚本操作这些目录及其子目录
时(该目录必须在include_path中或者用完整路径来包含), 则允许越过UID/GID检查,即不对
该目录进行UID/GID匹配性检查.
从PHP4.2.0开始, 这个指令已经可以接受同include_path指令类似的风格, 即用分号隔开的
多个路径, 而以前则只能指定单一个目录. 同open_basedir一样, 它所指定的路径实际上也
是一个字符串的前缀限制,而非针对该目录名称空间的操作.
例如如果指定: “safe_mode_include_dir = /dir/incl”, 那么所有的PHP脚本都将允许任意
访问 “/dir/include”和“/dir/incls” 路径(如果它们存在的话). 因此, 如果您希望将访问
控制在一个指定的目录里面, 就必须在上述设置的指定路径的结尾加上一个斜线, 例如:
“safe_mode_include_dir = /dir/incl/”
请注意: VirtualHost会自动继承php.ini中的safe_mode_include_dir设置.
================================================================================
================================================================================
(3) open_basedir: 将用户可操作的文件限制在某目录下;
——————————————————————————–
如下是php.ini中的原文说明以及默认配置:
; open_basedir, if set, limits all file operations to the defined directory
; and below. This directive makes most sense if used in a per-directory or
; per-virtualhost web server configuration file. This directive is
; *NOT* affected by whether Safe Mode is turned On or Off.
open_basedir = .
open_basedir可将用户访问文件的活动范围限制在指定的区域,通常是其家目录的路径,也
可用符号”.”来代表当前目录。注意用open_basedir指定的限制实际上是前缀,而不是目录名。
举例来说: 若”open_basedir = /dir/user”, 那么目录 “/dir/user” 和 “/dir/user1″都是
可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。例如设置成:
“open_basedir = /dir/user/”
open_basedir也可以同时设置多个目录, 在Windows中用分号分隔目录,在任何其它系统中用
冒号分隔目录。当其作用于Apache模块时,父目录中的open_basedir路径自动被继承。
有三种方法可以在Apache中为指定的用户做独立的设置:
(a) 在Apache的httpd.conf中Directory的相应设置方法:
php_admin_value open_basedir /usr/local/apache/htdocs/
#设置多个目录可以参考如下:
php_admin_value open_basedir /usr/local/apache/htdocs/:/tmp/
(b) 在Apache的httpd.conf中VirtualHost的相应设置方法:
php_admin_value open_basedir /usr/local/apache/htdocs/
#设置多个目录可以参考如下:
php_admin_value open_basedir /var/www/html/:/var/tmp/
(c) 因为VirtualHost中设置了open_basedir之后, 这个虚拟用户就不会再自动继承php.ini
中的open_basedir设置值了,这就难以达到灵活的配置措施, 所以建议您不要在VirtualHost
中设置此项限制. 例如,可以在php.ini中设置open_basedir = .:/tmp/, 这个设置表示允许
访问当前目录(即PHP脚本文件所在之目录)和/tmp/目录.
请注意: 若在php.ini所设置的上传文件临时目录为/tmp/, 那么设置open_basedir时就必须
包含/tmp/,否则会导致上传失败. 新版php则会提示”open_basedir restriction in effect”
警告信息, 但move_uploaded_file()函数仍然可以成功取出/tmp/目录下的上传文件,不知道
这是漏洞还是新功能.
================================================================================
================================================================================
(4) disable_functions: 单独地屏蔽某些函式(常用于禁止普通用户执行系统函数);
——————————————————————————–
这个指令允许你基于安全原因直接禁止某些确定的函式(通常是攸关系统安全的函数),例如:
disable_functions = shell_exec,system,exec,passthru,show_source,get_cfg_var
disable_functions接受逗号分隔的函式名列表作为参数, 它不受安全模式的影响,而且只能
设置在php.ini中用作全局性配置, 不能将其设置在httpd.conf中针对单独用户来进行设置.
从php-4.0.1开始在php.ini里引入了此项功能, 这个功能非常有用, 可以用它禁止用户使用
一些具有潜在的危险性的函数, 例如: passthru,exec,system,popen 等等. 当您在php.ini
中加上 disable_functions = passthru,exec,system,popen 配置后, PHP在执行这些函数
时就只会显示错误提示: Warning: system() has been disabled for security reasons
下面举个例子来看看这个安全性设置的重要程度:
我们知道PHP脚本可以采用很多perl的特性,例如通过一种叫shell_exec的方法来执行系统的
命令, 只需在一对反引号(“)中包含调用系统命令的script代码, 就能执行相应的系统命令.
例如:
$output = `ls /etc -al`;
echo $output;
?>
显然,如果您的系统不加限制的话,那么任何用户都可以通过诸如 `cat /etc/passwd` 这样的
命令攫取系统信息或进行破坏行为. 这对于提供公共服务的ISP供应商来说, 等于是打开自家
金库大门让所有客户自由出入, 而且一旦某些客户的PHP程序存在安全漏洞(就目前PHP程序员
的平均水准来看,存在严重的漏洞基本上是不可避免的)的话, 那么只需最低级的黑客都可以
很简单地完全操控您的主机了. 因此一定要防止Linux用户在PHP程序中通过“来执行script
脚本,这可以通过在PHP.INI中设置: disable_functions = shell_exec,system,exec 来禁止
PHP调用相关系统函数.
典型的安全性配置,请参考如下设置:
disable_functions = shell_exec,system,exec,passthru,show_source,get_cfg_var
若允许用户调试程序,则可配置如下:
disable_functions = shell_exec,system,exec,passthru
PHP中一些常用但有安全风险的函数:
unlink,mkdir,touch,fgets,popen,proc_open,link,symlink,phpinfo
建议: 应兼顾到商业服务的完整性和安全性,请酌情考虑是否禁止使用它们.
请注意: disable_functions选项不能在php.ini文件外部使用,也就是说您无法在httpd.conf
文件中按不同虚拟主机或不同目录的方式来屏蔽或者开启函式。
================================================================================
================================================================================
5) register_globals: 禁止注册全局变量;
——————————————————————————–
register_globals = On (自动注册为全局变量)
register_globals = Off (不可注册为全局变量)
一般情况下,用户都是在HTML网页里通过HTTP协议,来提交GET,POST和COOKIE数据(简称为GPC)
的. 而PHP程序如何获得用户提交的这些变量数据,则还需依赖于php.ini配置中一个有争议的
设置,即是register_globals参数来决定.
顾名思义,register_globals的意思就是注册为全局变量, 所以当设置为On的时候, 通过页面
传递过来的值就会被直接的注册为全局变量,可以很方便地提供给PHP程序直接使用;而当设置
为Off的时候,PHP程序要使用网页传递过来的变数,就需要到特定的数组里才能得到它.
在PHP4.3.0以后,register_globals默认情况下被设置为Off; 但是几年前,register_globals
的默认值还是打开的,所以现在依然还存在很多需要启用它的程序代码.
请注意: 当设置为 register_globals = Off 之后,不仅会影响到PHP如何获取从
的URL
所传递过来的数据,也会影响到PHP获取session和cookie的方式. 当关闭register_globals时,
PHP程序就必须使用相应的数组来获取session和cookie(例如$_SESSION[]和$_COOKIE). 同时
对于session的处理也有一些改变,比如使用session_register()就显得再也没有必要了,而且
会失效(关于具体的变化请查看PHP手册页的Session handling functions文档之描述).
启用register_globals本身并无安全风险,但是它为跟踪用户输入和确保应用程序安全增加了
难度. 因为一旦打开了register_globals,那么在全局名称空间和$_GET,$_POST或$_COOKIE的
数组中,将会自动创建GET,POST和COOKIE传递到PHP脚本的所有变量. 如果您的PHP程序利用了
这些变量来作安全标识(例如很多程序员喜欢用COOKIE值来辨别用户身份),那么任何人都可以
用通过URL所传递的数值来获取并假冒用户身份,显然这就不再具备安全性了.
另外,一些粗枝大叶或没有责任心的程序员所写的PHP代码, 例如: ,
也很可能让黑客或恶意使用者有机可乘,使他们得以利用register_globals的弱点来进行诸如
代码注入或内存溢位等方式的攻击,从而很轻易地造成系统性的灾难.
重要提示: 自PHP4.2.0起,PHP中的选项register_globals的默认值被设为off了,PHP社区鼓励
大家不要依赖于这个选项,而用其它方法来替代,例如superglobals。
在Apache的httpd.conf中VirtualHost的相应设置方法:
php_admin_flag register_globals on
或者:
php_admin_value register_globals 1
================================================================================
================================================================================
6) magic_quotes_gpc: 令敏感字元转义
——————————————————————————–
magic_quotes_gpc = On
magic_quotes_gpc = Off
magic_quotes_gpc选项是php中的一个重要的安全设置, 当该选项为ON, 也就是打开的时候,
所有从GET,POST,COOKIE传递过来的数据之中的特殊字元(如’”/等),以及NULL等元字符都会
被自动的加上/以实现转义,这个选项使得SQL注入或者插入代码,以及XSS中引入字符串或者
改变程序流程变得更加困难。
在php.ini配置文件中是默认启用magic_quotes_gpc设置(即为On)的,这相当于自动对所提交
的GET,POST,COOKIE数据使用了addslashes()函数. 如果网站空间关闭了 magic_quotes_gpc
设置, 那么PHP就不会在敏感字元前加上反斜杠(/), 即允许表单所提交的内容含有敏感字元,
例如单引号(’)等等, 这就更容易让黑客或者恶意使用者有机会利用SQL的注入漏洞发动攻击.
当然现在很多具备安全意识的程序员或数据库管理员都懂得如何防范SQL注入攻击,他们通常
会在相应的程序代码或应用环境中加强安全防范, 但是无论如何, 在系统层级加强安全防范,
始终都应该是LINUX系统管理员的不二责任.
请注意: PHP程序代码中关于敏感字元的处理,可以用addslashes()来自动在敏感字元前添加
反斜杠, 也可以用函数stripslashes()来去掉反斜线. 另外, 许多数据库本身也提供了针对
这种输入数据的处理功能. 例如在PHP版本的MySQL操作函数中, 就有一个调用数据库来处理
输入数据的函数: mysql_real_escape_string(); 它可将特殊字符以及可能引起数据库操作
出错的字符转义.
在Apache的httpd.conf中VirtualHost的相应设置方法:
php_admin_flag magic_quotes_gpc on
或者:
php_admin_value magic_quotes_gpc 1
================================================================================
================================================================================
7) allow_url_fopen和allow_url_include: 禁止读取远程文件
——————————————————————————–
allow_url_fopen = On (允许打开URL文件,预设启用)
allow_url_fopen = Off (禁止打开URL文件)
allow_url_include = Off (禁止引用URL文件,新版增加功能,预设关闭)
allow_url_include = On (允许引用URL文件,新版增加功能)
allow_url_fopen 这个命令选项启动了URL形式的fopen封装协议, 使得PHP程序可以连接URL
对象(如远端文件). 预定的封装协议提供用ftp和http协议来连接远程文件,一些扩展库例如
zlib可能会注册更多的封装协议. allow_url_include预计是下一个PHP版本将要提供的功能,
用来分离fopen和include函数的远端调用,现在PHP5.20已经提供了这个选项.
就性能方面来说,PHP所提供的方便的远程调用确实简化了很多应用, 但若是从安全角度来看,
允许引用(Include)URL远端资源,使得PHP应用程序的漏洞变得更加容易被利用, 这种方便性
反而被很多安全研究人员视为一种漏洞(Remote URL Include vulnerabilities), 因此常常
被建议必须在php.ini配置中禁止使用.
PHP的开发者计划在PHP6版本中提供allow_url_include,现在这个功能已经可以在PHP5.20版
中应用. 禁止allow_url_include解决了远端引用(Include)的问题, 同时又让我们还可以在
一般的情形下使用fopen去打开远端的档案, 而不必再牵连上打开include函数所带来的风险.
因此在新版PHP中allow_url_fopen选项预设是打开的,而allow_url_include则预设是关闭的.
然而事实上若从系统角度来看,即使禁止了PHP的allow_url_fopen和allow_url_include功能,
其实也不能完全阻止远端调用及其所带来的安全隐忧,而且它们只是保护了标记为URL的句柄,
也就是说只能影响http(s)和ftp(s)的调用, 但对包含其他标记的远端调用,例如对PHP5.2.0
新版所提供的php和data则无能为力,而这些调用一样会导致注入风险,请参考如下代码:
// The following Include statement will
// include and execute everything POSTed
// to the server
include "php://input";
?>
// The following Include statement will
// include and execute the base64 encoded
// payload. Here this is just phpinfo()
include "data:;base64,PD9waHAgcGhwaW5mbygpOz8+";
?>
当然在LINUX的系统层级上,还有别的办法可用来防止远端调用, 例如使用IPTABLES等防火墙
工具来保护系统,另外PHP应用程序也可以考虑采用curl来读取远程文件.
请注意: 只在PHP4.0.3之后的版本中才可以在php.ini配置文件中使用allow_url_fopen选项,
在PHP4.0.3以及之前的版本, 则只能在编译时通过配置项 –disable-url-fopen-wrapper来
取消此特性. Windows下的PHP在4.3版本之前,相关函式: include, include_once, require,
require_once 不支援远程文件连接,在PHP4.3版本之后才可以让这类函式有远端读取的能力.
在Apache的httpd.conf中VirtualHost的相应设置方法:
php_admin_flag allow_url_fopen Off
php_admin_flag allow_url_include Off
或者:
php_admin_value allow_url_fopen 0
php_admin_value allow_url_include 0
================================================================================
================================================================================
Error handling and logging: 错误控制和日志
——————————————————————————–
display_errors = On (打开错误显示)
display_errors = Off (关闭错误显示)
PHP缺省是打开错误信息显示的,如果把它改为关闭之后, 那么当PHP函数执行时,其错误信息
将不会再显示给用户,这样能在一定程度上防止攻击者从错误信息得知脚本的物理位置,以及
一些其它有用的信息,起码给攻击者的黑箱检测造成一定的障碍.
如果PHP的错误信息对我们自己有用,也可以设置成把它写到日志文件中去,例如:
log_errors = Off (PHP 默认是关闭错误日志的)
log_errors = On (修改为打开并记录错误日志)
如果打开了日志记录,接着还需要指定日志文件,即告诉PHP将错误记录到那个文件中去:
;error_log = filename (默认被分号”;”所注释,修改为如下)
error_log = /var/log/php_error.log
就是把filename改为指定文件”/var/log/php_error.log”, 这样设置以后,所有的PHP错误都
将会写到这个日志文件裡去。
在Apache的httpd.conf中VirtualHost的相应设置方法:
php_admin_flag display_errors Off
或者:
php_admin_value display_errors 0
为方便区分,可在每个用户的VirtualHost中指定错误日志文件:
ErrorLog logs/mydomain.com-error_log
================================================================================
################################################################################
PHP安全配置范例:
################################################################################
================================================================================
php.ini的安全设置范例:
——————————————————————————–
safe_mode = On
allow_url_fopen = Off
allow_url_include = Off
register_globals = Off
magic_quotes_gpc = On
display_errors = Off
disable_functions = shell_exec,system,exec,passthru,show_source,get_cfg_var
#或者,也可以考虑开放后两个危险系数较低的函数:
disable_functions = shell_exec,system,exec,passthru
open_basedir = .
——————————————————————————–
VirtualHost的一个配置范例:
——————————————————————————–
ServerAdmin [email]webmaster@mydomain[/email]
DocumentRoot /home/hosting/mydomain/public_html
ServerName mydomain.com
ServerAlias www.mydomain.com
php_admin_value safe_mode 1
php_admin_value allow_url_fopen 0
php_admin_value allow_url_include 0
php_admin_value register_globals 1
php_admin_value magic_quotes_gpc 1
php_admin_value display_errors 0
php_admin_value open_basedir /home/hosting/mydomain/
ErrorLog logs/mydomain.com-error_log
CustomLog logs/mydomain.com-access_log common
#或者:
ServerAdmin [email]webmaster@mydomain[/email]
DocumentRoot /home/hosting/hung25ucom/public_html
ServerName mydomain.com
ServerAlias www.mydomain.com
php_admin_flag safe_mode On
php_admin_flag allow_url_fopen Off
php_admin_flag allow_url_include Off
php_admin_flag register_globals On
php_admin_flag magic_quotes_gpc On
php_admin_flag display_errors Off
php_admin_value open_basedir /home/hosting/mydomain/
ErrorLog logs/mydomain.com-error_log
CustomLog logs/mydomain.com-access_log common