【问题标题】:PHP `require_once` includes wrong filePHP `require_once` 包含错误的文件
【发布时间】:2015-06-18 09:48:16
【问题描述】:

我在这样的 Linux Ubuntu 14.04-LTS 机器上有一个开发树,具有三个相同的分支:

main -+-- leonardo --- project --- htdocs -+- panel --- index.php
      |                                    |
      |                                    +- config.php
      |
      +-- federico --- project --- htdocs -+- panel --- index.php
      |                                    |
      |                                    +- config.php
      |
      +-- carlo ------ project --- htdocs -+- panel --- index.php
      |                                    |
      |                                    +- config.php
    ..... (you get my drift).

既没有软链接也没有硬链接config.php 文件在 svn-ignore 中并且在所有分支之间是不同的

有一个 Apache 服务器,每个开发人员都有一个虚拟主机,所以我可以在 http://leonardo.project.local 或 Federico 的 http://federico.project.local 上查看我的开发版本。

在调查当前的怪异时,这两个文件是:

<?php // this is panel/index.php
    echo "I am " . __FILE__ . "\n";
    echo "I will include " . realpath('../config.php') . "\n";
    require_once '../config.php';

<?php // this is config.php
    echo "I am " . __FILE__ . "\n";
    exit();

当然,预期的输出是:

I am leonardo/project/htdocs/panel/index.php
I will include /var/www/main/leonardo/project/htdocs/config.php
I am leonardo/project/htdocs/config.php

但实际输出是:

I am leonardo/project/htdocs/panel/index.php
I will include /var/www/main/leonardo/project/htdocs/config.php
I am federico/project/htdocs/config.php

另外一个奇怪的地方是

    echo "I will include " . realpath('../config.php') . "\n";
    require_once realpath('../config.php');

有效。

TL;DR require_oncerealpath 不同意 '../config.php' 的实际位置。

真正奇怪的是我没有看到如何leonardo/project/htdocs/panel/ 中运行的脚本可以知道关于federico/project/htdocs/config.php;它应该向上四个目录,然后探索很多子目录。

我几乎开始怀疑这可能与文件系统甚至内核相关。

文件系统是ext4,内核是3.13.0-55-generic #92-Ubuntu SMP Sun Jun 14 18:32:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux。该机器是最新 VMware Workstation 上的虚拟 x64。

检查

  • PHP 的include_path 只包括./usr/local/php5/pear
  • 如前所述,分支中没有文件是符号链接,所有相关文件的 inode 计数表明没有交叉链接。文件确实不同。
  • 所有文件都确实存在,这不是“最后的包含”。
  • 从命令行,在 leonardo...面板中,我运行“cat ../config.php”并得到我的 config.php,正如预期的那样。只有 PHP 会包含错误的文件。
  • 重新启动 Apache(以防万一)无济于事。接下来我将尝试重新启动整个虚拟机,但要做到这一点,我需要冻结几个服务,这需要一段时间。
  • 到昨天为止,一切都很糟糕(那时我不在这里)。在过去的三天里,没有系统更新,没有重新启动,甚至没有远程登录。现在正常运行时间为八天。
  • 我是个白痴:我可以通过检查集成测试日志知道何时开始发生这种情况。已经要求他们,午餐后期待他们。

【问题讨论】:

  • 某种操作码缓存在干扰?
  • 系统确实使用opcache。但我希望它已被 Apache 重新启动清除......?我对opcache操作不熟悉;它是否在某处使用共享内存或临时文件?
  • @Blizz,我发现在集成测试开始出错之前,opcache.ini 文件已被有缺陷的同步脚本完全修改。在那之后,很容易发现血淋淋的细节。如果您发布有关该效果的答案,我很乐意用故事来扩展它并接受。事实证明,虽然重新启动 Apache 确实可以解决问题,但其他正在运行的脚本可能会再次破坏它们,太快以至于无法意识到它们已修复。

标签: php linux ext4


【解决方案1】:

值得检查您的include_path 设置为什么(这可以使用get_include_path 完成)。

requireinclude 在给定绝对路径和相对路径的情况下会表现不同。当你使用

require_once realpath('../config.php');

这是这样做的:

require_once '/var/www/main/leonardo/project/htdocs/config.php';

如您所愿。

以下的奇怪之处:

require_once '../config.php';

发生是因为 PHP 将检查包含路径中的每个条目以查找匹配文件并返回第一个匹配条目。因此,很可能首先检查 federico 配置的路径。

【讨论】:

  • 谢谢:我的错,我没有说明我已经先检查了。您的回答总体上很有用,不是您的错,而是我的错,如果现在对我没有帮助。因此,我的感谢和我的赞成:-)
【解决方案2】:

您是否检查过任何潜在的操作码缓存及其设置? 过去我在那里遇到过一些问题,例如没有检测到更改的文件。

具体来说,如果opcache.use_cwd 设置为零,这种情况可能而且将会发生。

opcache.use_cwd 布尔值

如果启用,OPcache 会将当前工作目录附加到脚本键,从而消除文件之间可能的冲突 相同的基本名称。禁用此指令可提高性能, 但可能会破坏现有应用程序

如果发生这种情况,那么第一个用户或 phpunit 脚本访问不同目录中给定名称的文件(例如 leonardo/config.phpfederico/config.php)将用该文件“填充”缓存。 realpath 等文件系统功能不受影响,将继续工作。使用绝对路径的引用将继续工作。 相对路径的引用会以一种非常隐蔽的方式被破坏

因为在你只有一个人工作之前,那个人已经为他的需要准备好了缓存,并且不会注意到任何事情。然后回来工作,你开始加载他的文件。

附带说明,该设置可能会导致无意的信息泄露,因为该设置是系统范围的。因此,您知道您的 ISP 的 use_cwd 损坏,您知道另一个站点包含“../inc/credit_cards.php”,您在 your 站点中准备了相同的路径并包含具有相同的文件姓名。 get_defined_vars() 可能让你 pwn 其他站点的登录或数据库系统。 (没有检查,但考虑到发生了什么,不明白为什么不)。

更新

我检查了 PHP5.6 和旧备份 VM 上的上述配置(我们有过去五年的时间机器快照 :-))。 我确实能够在我无权访问的不同虚拟主机中读取所有已定义的全局变量,从而覆盖身份验证。 This problem was discovered in 2016 但他们想到的测试并没有扩展到通过创建同名文件来欺骗 opcache 包含其他人的文件。

我还在我们当前的开发虚拟机上重新运行了相同的测试,并且问题似乎已经消失,即使配置发生了很大变化,我不确定他们'不再具有可比性。

我正在考虑将共享主机 config.inc.php 文件重命名为 config.a72b1qTy.inc.php 之类的名称。

【讨论】:

    猜你喜欢
    • 2012-08-27
    • 2014-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多