首页 > 解决方案 > 如何让 PHP 包含数据 URI(不打开 allow_url_include)?

问题描述

在我的 PHP 设置中,出于安全原因,我已经allow_url_include设置为FALSE并且我很高兴保持这种方式。

当我想

include 'https://example.com/path/to/include.php'; // <= THIS WON'T WORK

我可以用:

include $_SERVER['DOCUMENT_ROOT'].'/path/to/include.php'; // <= BUT THIS WILL

到目前为止,一切都很好。


然而,现在,我正在尝试在 PHP 运行时创建include 虚拟文件的方法。

我想知道 PHP 中是否可能有与 javascript 等效的东西URL.createObjectURL(),之后我发现 PHP 可以读取data:URI。

这听起来很有希望......但因为我已经allow_url_include设置为FALSE,PHP 告诉我它不能include以 URI 开头data:

警告:包装器在服务器配置中被禁用 include(): data://allow_url_include=0

所以,而不是:

include 'data:application/x-php;base64,ZnVuY3Rpb24gdGVzdDIoKSB7ZWNobyAnVGhpcyB0ZXN0IGZ1bmN0aW9uIDIgaXMgd29ya2luZy4nO30gdGVzdDIoKTs=';

我必须使用file_get_contents并且eval喜欢这样:

$Test_Function = file_get_contents('data:application/x-php;base64,ZnVuY3Rpb24gdGVzdDIoKSB7ZWNobyAnVGhpcyB0ZXN0IGZ1bmN0aW9uIDIgaXMgd29ya2luZy4nO30gdGVzdDIoKTs=');

eval("$Test_Function");

后一种方法有效,但是(尤其是因为它很慢)我不想使用eval(),除非我绝对必须这样做。

有什么方法可以让 PHP 访问includeURI data:而无需打开allow_url_include


注1:

您可能会对我在这里使用 base-64 编码的内容感到好奇:

ZnVuY3Rpb24gdGVzdDIoKSB7ZWNobyAnVGhpcyB0ZXN0IGZ1bmN0aW9uIDIgaXMgd29ya2luZy4nO30gdGVzdDIoKTs=

它只是以下内容:

function test2() {echo 'This test function 2 is working.';} test2();

笔记2:

当我第一次尝试创建数据 URI 时(早在我在这里发布之前),我在百分比编码方面犯了一个错误,使数据 URI 无效。

我尝试对 Data URI 进行 base-64 编码(这很有效),之后我没有返回到原始百分比编码的 Data URI 来找出我哪里出错了。

但是,重要的是要注意,根本没有理由(除了混淆)在 Data URI 中使用 base-64编码

以下百分比编码的数据 URI 在功能上是相同的:

data:application/x-php,function%20test2%28%29%20%7Becho%20%27This%20test%20function%202%20is%20working.%27%3B%7D%20test2%28%29%3B

注3:

include虽然到目前为止,我在Data URI上使用allow_url_include仍然设置为时没有成功,但FALSE我想我会比较:

  1. 编写一个字符串并eval()在该字符串上运行以执行它
  2. 创建一个文件并include在该文件上运行

一项测试包括运行其中一个进程超过 10,000 次。我没有运行一次测试,而是连续运行了一组 6 个测试。总的来说,我跑了 5 组。

这涉及 10,000 个过程的 5 组 6 次测试。

综上所述:

因此,任何花费eval()多达 100 倍时间来解析和执行的字符串,仍然只需要不到百分之一秒( < 0.01s) 即可完成。

所以......如果我找不到任何方法到includeData URI,我会继续eval(),尽管它比 PHP 的正常速度慢 10 倍以上,但它仍然非常棒。


这里(为了完整起见)是注释 3中测试的结果(以秒为单位) :

设置 1

PHP 脚本(控制)(x 10000)

评估测试 (x 10000)

创建文件测试 (x 10000)

设置 2

PHP 脚本(控制)(x 10000)

评估测试 (x 10000)

创建文件测试 (x 10000)

设置 3

PHP 脚本(控制)(x 10000)

评估测试 (x 10000)

创建文件测试 (x 10000)

设置 4

PHP 脚本(控制)(x 10000)

评估测试 (x 10000)

创建文件测试 (x 10000)

设置 5

PHP 脚本(控制)(x 10000)

评估测试 (x 10000)

创建文件测试 (x 10000)

标签: phpdata-uri

解决方案


我闻到了X/Y 问题- 您已经将URI 视为特定问题的可能解决方案,evaldata:发现了两者的缺点,但没有回到最初的问题陈述。

从各种评论中,我们可以将问题定义为找到一种机制:

  1. 可以执行任意动态代码
  2. 具有与正常类似的解析性能include
  3. 将允许代码缓存在 OpCache 中
  4. 不需要在其他地方放松安全措施

您说eval在第 2 点(因为它启动了一个新的解析器)和第 3 点(因为没有可供 OpCache 使用的密钥)失败。

您已经确定data:URI 在第 4 点失败(因为它们需要allow_url_include),但我怀疑它们也在第 2 点失败(因为 base64 解码是不小的开销)和第 3 点(因为 OpCache 不支持缓存任意流;我相信它只支持文件路径和 PHAR 内容)

正如另一个答案中所建议的那样,更好的整体解决方案是将代码写入“真实”文件,使用内存中的文件系统tmpfs来提高性能。这符合上述所有四个标准,因为就 PHP 而言,它与任何其他源文件相同;唯一的额外开销是将文件写入虚拟文件系统的 I/O 速度。

关键的优化将确保您跟踪动态内容何时发生更改,从某种修订 ID 或字符串的哈希生成唯一的文件路径。这可以实现两件事:

  1. 您可以跳过写入磁盘上已存在的文件,从而节省 I/O 时间
  2. OpCache 将能够缓存此文件的内容并重用它,即使您设置opcache.validate_timestamps为 false ,您也可以确信运行正确的代码

推荐阅读