2009年11月23日星期一

PHP 安全模式

PHP 的安全模式是为了试图解决共享服务器(shared-server)安全问题而设立的。在结构上, 试图在 PHP 层上解决这个问题是不合理的, 但修改 web 服务器层和操作系统层显得非常不现实。因此许多人, 特别是 ISP, 目前使用安全模式。
保安措施和安全模式

保安措施和安全模式配置指令

































































名称 默认值 可修改范围 更新记录
safe_mode 0 PHP_INI_SYSTEM
safe_mode_gid 0 PHP_INI_SYSTEM 自 PHP 4.1.0 起可用
safe_mode_include_dir NULL PHP_INI_SYSTEM 自 PHP 4.1.0 起可用
safe_mode_exec_dir PHP_INI_SYSTEM
safe_mode_allowed_env_vars PHP_ PHP_INI_SYSTEM
safe_mode_protected_env_vars LD_LIBRARY_PATH PHP_INI_SYSTEM
open_basedir NULL PHP_INI_SYSTEM
disable_functions 仅 php.ini 自 PHP 4.0.1 起可用
disable_classes 仅 php.ini 自 PHP 4.3.2 起可用

PHP_INI_* 常量的进一步详细说明与定义见 ini_set()
以下是配置选项的简要解释。



  • safe_mode boolean
    默认情况下, 安全模式在打开文件时会做 UID 比较检查。如果想将其放宽到 GID 比较, 则打开 safe_mode_gid。是否在文件访问时使用 UID ( FALSE )或者 GID ( TRUE )来做检查。

  • safe_mode_gid boolean
    默认情况下, 安全模式在打开文件时会做 UID 比较检查。如果想将其放宽到 GID 比较, 则打开 safe_mode_gid。是否在文件访问时使用 UID ( FALSE )或者 GID ( TRUE )来做检查。


  • safe_mode_include_dir string
    当从此目录及其子目录(目录必须在 include_path 中或者用完整路径来包含)包含文件时越过 UID / GID 检查。
    从 PHP 4.2.0 开始, 本指令可以接受和 include_path 指令类似的风格用冒号(Windows 中是分号)隔开的路径, 而不只是一个目录。
    指定的限制实际上是一个前缀, 而非一个目录名。这也就是说 safe_mode_include_dir = /dir/include将允许访问/dir/include/dir/incls, 如果它们存在的话。如果希望将访问控制在一个指定的目录, 那么需在结尾加上一个斜线, 例如: safe_mode_include_dir = /dir/incl/.
    如果本指令的值为空, 在 PHP 4.2.3 中以及 PHP 4.3.3 起具有不同 UID / GID 的文件将不能被包含。在较早版本中, 所有文件都能被包含。


  • safe_mode_exec_dir string
    如果 PHP 使用了安全模式, system() 和其它程序执行函数将拒绝启动不在此目录中的程序。必须使用 / 作为目录分隔符, 包括 Windows 中


  • safe_mode_allowed_env_vars string
    设置某些环境变量可能是潜在的安全缺口。本指令包含有一个逗号分隔的前缀列表。在安全模式下, 用户只能改变那些名字具有在这里提供的前缀的环境变量。默认情况下, 用户只能设置以 PHP_ 开头的环境变量(例如 PHP_FOO = BAR) >如果本指令为空, PHP 将使用户可以修改任何环境变量


  • safe_mode_protected_env_vars string
    本指令包含有一个逗号分隔的环境变量的列表, 最终用户不能用 putenv() 来改变这些环境变量。甚至在 safe_mode_allowed_env_vars 中设置了允许修改时也不能改变这些变量

  • open_basedir string
    将 PHP 所能打开的文件限制在指定的目录树, 包括文件本身。本指令不受 安全模式打开或者关闭的影响。
    当一个脚本试图用例如 fopen() 或者 gzopen() 打开一个文件时, 该文件的位置将被检查。当文件在指定的目录树之外时 PHP 将拒绝打开它。所有的符号连接都会被解析, 所以不可能通过符号连接来避开此限制。
    特殊值 . 指明脚本的工作目录将被作为基准目录。但这有些危险, 因为脚本的工作目录可以轻易被 chdir() 而改变。
    在 httpd.conf 文件中中, open_basedir 可以像其它任何配置选项一样用php_admin_value open_basedir none的方法关闭(例如某些虚拟主机中)。
    在 Windows 中, 用分号分隔目录, 在任何其它系统中用冒号分隔目录。作为 Apache 模块时, 父目录中的 open_basedir 路径自动被继承。
    用 open_basedir 指定的限制实际上是前缀, 不是目录名。也就是说open_basedir = /dir/incl也会允许访问/dir/include/dir/incls, 如果它们存在的话。如果要将访问限制在仅为指定的目录, 用斜 线结束路径名。eg:open_basedir = /dir/incl/支持多个目录需要 PHP > 3.0.7, 默认是允许打开所有文件。


  • disable_functions string

    本指令允许你基于 安全 原因禁止某些函数。接受逗号分隔的函数名列表作为参数。 disable_functions 不受 安全模式 的影响。
    本指令只能设置在 php.ini 中。例如不能将其设置在 httpd.conf 。


  • disable_classes
    本指令可以使你出于 安全 的理由禁用某些类。用逗号分隔类名。disable_classes 不受 安全模式 的影响。
    本指令只能设置在 php.ini 中。例如不能将其设置在 httpd.conf 。 PHP > 4.3.2



参见 register_globals , display_errors log_errors
当 safe_mode 设置为 on, PHP 将通过文件函数或其目录检查当前脚本的拥有者是否和将被操作的文件的拥有者相匹配。eg:
[bash]
-rw-rw-r-- 1 rasmus rasmus 33 Jul 1 19:20 script.php
-rw-r--r-- 1 root root 1116 May 26 18:01 /etc/passwd
[/bash]

[php title="运行 script.php"]
readfile ( '/etc/passwd' );
[/php]

如果安全模式被激活, 则将会导致以下错误:
[text]
Warning: SAFE MODE Restriction in effect. The script whose uid is 500 is not
allowed to access /etc/passwd owned by uid 0 in /docroot/script.php on line 2
[/text]

同时, 或许会存在这样的环境, 在该环境下, 宽松的 GID 检查已经足够, 但严格的 UID 检查反而是不适合的。可以用 safe_mode_gid 选项来控制这种检查。如果设置为 On 则进行宽松的 GID 检查;设置为 Off (默认值)则进行 UID 检查。
除了 safe_mode 以外, 如果设置了 open_basedir 选项, 则所有的文件操作将被限制在指定的目录下. eg:
[bash]
<Directory /docroot>
php_admin_value open_basedir /docroot
</Directory>
[/bash]
如果在设置了 open_basedir 选项后运行同样的 script.php, 则其结果会是:
[text]
Warning: open_basedir restriction in effect. File is in wrong directory in
docroot/script.php on line 2
[/text]

也可以单独地屏蔽某些函数。请注意 disable_functions 选项不能在 php.ini 文件外部使用, 也就是说无法在 httpd.conf 文件的按不同虚拟主机或不同目录的方式来屏蔽函数。如果将如下内容加入到 php.ini 文件:
[bash]
disable_functions readfile,system
[/bash]

则会得到如下的输出:
[text]
Warning: readfile() has been disabled for security reasons in
/docroot/script.php on line 2
[/text]









警告
当然, 这些 PHP 限制不适用于可执行文件。

被安全模式限制或屏蔽的函数
以下 安全模式 列表可能不完整或不正确。

安全模式限制函数













































































































































































函数名 限制
dbmopen() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
dbase_open() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
filepro() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
filepro_rowcount() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
filepro_retrieve() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
ifx_* sql_safe_mode 限制, (!= safe mode)
ingres_* sql_safe_mode 限制, (!= safe mode)
mysql_* sql_safe_mode 限制, (!= safe mode)
pg_loimport() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
posix_mkfifo() 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
putenv() 遵循 ini 设置的 safe_mode_protected_env_vars 和 safe_mode_allowed_env_vars 选项。请参考 putenv() 函数的有关文档。
move_uploaded_file() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
chdir() 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
dl() 本函数在 安全模式 下被禁用。
backtick operator 本函数在 安全模式 下被禁用。
shell_exec() (在功能上和 backticks 函数相同) 本函数在 安全模式 下被禁用。
exec() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因, 目前不能在可执行对象的路径中使用 .. 。 escapeshellcmd() 将被作用于此函数的参数上。
system() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因, 目前不能在可执行对象的路径中使用 .. 。 escapeshellcmd() 将被作用于此函数的参数上。
passthru() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因, 目前不能在可执行对象的路径中使用 .. 。 escapeshellcmd() 将被作用于此函数的参数上。
popen() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因, 目前不能在可执行对象的路径中使用 .. 。 escapeshellcmd() 将被作用于此函数的参数上。
fopen() 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
mkdir() 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
rmdir() 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
rename() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
unlink() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
copy() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者). (on source and target )
chgrp() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
chown() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者).
chmod() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 另外, 不能设置 SUID、SGID 和 sticky bits
touch() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者).
symlink() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者). (注意:仅测试 target)
link() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者). (注意:仅测试 target)
apache_request_headers() 在安全模式下, 以“authorization”(区分大小写)开头的标头将不会被返回。
header() 在安全模式下, 如果设置了 WWW-Authenticate , 当前脚本的 uid 将被添加到该标头的 realm 部分。
PHP_AUTH 变量 在安全模式下, 变量 PHP_AUTH_USER 、 PHP_AUTH_PW 和 PHP_AUTH_TYPE 在 $_SERVER 中不可用。但无论如何, 您仍然可以使用 REMOTE_USER 来获取用户名称(USER)。(注意:仅 PHP 4.3.0 以后有效)
highlight_file() , show_source() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者). (注意, 仅在 4.2.1 版本后有效)
parse_ini_file() 检查被操作的文件或目录是否与正在执行的脚本有相同的 UID(所有者). 检查被操作的目录是否与正在执行的脚本有相同的 UID(所有者). (注意, 仅在 4.2.1 版本后有效)
set_time_limit() 在 安全模式 下不起作用。
max_execution_time 在 安全模式 下不起作用。
mail() 在安全模式下, 第五个参数被屏蔽。(注意, 仅自 PHP 4.2.3 起受影响)
任何使用 php4/main/fopen_wrappers.c 的函数  

没有评论:

发表评论