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; }
答案2
这里描述了一种解决方法: https://github.com/Whissi/realpath_turbo
通过该扩展,您可以使用启用了 openbasedir 的 realpatch 缓存