PHP 安全模式/open_basedir-lstat 性能问题

PHP 安全模式/open_basedir-lstat 性能问题

PHP lstat 在读取文件之前多次显示完整路径。当在 apache httpd 配置中指定时,就会发生这种情况PHP_ADMIN_VALUE open_basedir 设置或者 safe_mode 是否为 ON

如果我有一个简单的网站,其中只有 phpinfo.php 页面,并且里面只有“”。

假设我们拥有最新版本的 httpd (2.2.15) 和 PHP (5.2.13 或 5.3.2)。

如果我们指定safe_mode=on 或 PHP_ADMIN_VALUE open_basedir在虚拟主机配置中:

<目录“/usr/local/myspace/webspace/httpdocs”>
                PHP_ADMIN_VALUE open_basedir "/usr/local/myspace/webspace"
</目录>
<虚拟主机 *:80>
        服务器名称 damorealt.xoom.it
        DocumentRoot“/usr/local/myspace/webspace/httpdocs”
    CustomLog /var/log/httpd/damorealt/access_log 合并
    错误日志 /var/log/httpd/damorealt/error_log
</虚拟主机>

呼叫页面http://damorealt.xoom.it/phpinfo.php我们可以重现以下行为:

首次检查

25933 lstat ("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat ("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace/webspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat(“/usr/local/myspace/webspace/httpdocs”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

第二次检查

25933 lstat ("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat ("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace/webspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat(“/usr/local/myspace/webspace/httpdocs”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

第三次检查(不完整)

25933 lstat ("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat ("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace/webspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0

第五次检查。

25933 lstat ("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat ("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat (“/usr/local/myspace/webspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat(“/usr/local/myspace/webspace/httpdocs”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
25933 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

阅读文件!

25933 打开(“/usr/local/myspace/webspace/httpdocs/phpinfo.php”,O_RDONLY)= 16
25933 fstat (16, {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
25933 读取(16,“\n”,8192)= 16
25933 读取(16,“”,8192)= 0
25933 读取(16,“”,8192)= 0
25933 关闭(16)= 0

如果删除了 PHP_ADMIN_VALUE open_basedir "/usr/local/myspace/webspace" :

首次检查

26235 时间(空) = 1278696735
26235 lstat ("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat ("/usr/local", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat (“/usr/local/myspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat(“/usr/local/myspace/webspace”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat(“/usr/local/myspace/webspace/httpdocs”, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
26235 lstat("/usr/local/myspace/webspace/httpdocs/phpinfo.php", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0

讀取文件。

26235 打开(“/usr/local/myspace/webspace/httpdocs/phpinfo.php”,O_RDONLY)= 16
26235 fstat (16, {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
26235 读取(16,“\n”,8192)= 16
26235 读取(16,“”,8192)= 0
26235 读取(16,“”,8192)= 0
26235 关闭(16)= 0
26235 uname ({sys="Linux", node="svilpar4", ...}) = 0
26235 时间(空) = 1278696735
26235 writev(15, [{"HTTP/1.1 200 OK\r\n日期:星期五,09 J"..., 173},[...]
26235 更改目录(“/”) = 0

有人能解释一下为什么 PHP 会有这样的行为吗?

答案1

如果设置了 safe_mode 或 open_basedir,则禁用 Realpath 缓存。这会大大降低 PHP 引擎的性能,并且这种行为可能会导致服务器瘫痪。尤其是因为缺乏文档!

查看 PHP 引擎 5.2.13 的源代码 main/main.c,您可以看到:

1292: /* 如果设置了 safe_mode 或 open_basedir,则禁用 realpath 缓存
*/
                如果 (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
                        CWDG(真实路径缓存大小限制)= 0;
                }

1769: /* 如果设置了 safe_mode 或 open_basedir,则禁用 realpath 缓存 */
        如果 (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
                CWDG(真实路径缓存大小限制)= 0;
        }

请参阅:http://bugs.php.net/bug.php?id=52312

答案2

这里描述了一种解决方法: https://github.com/Whissi/realpath_turbo

通过该扩展,您可以使用启用了 openbasedir 的 realpatch 缓存

相关内容