为了账号安全,请及时绑定邮箱和手机立即绑定

如何让 PHP 包含数据 URI(不打开 allow_url_include)?

如何让 PHP 包含数据 URI(不打开 allow_url_include)?

PHP
扬帆大鱼 2023-04-28 15:13:13
在我的 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?注一:您可能对我在这里使用 base-64 编码的内容感到好奇:ZnVuY3Rpb24gdGVzdDIoKSB7ZWNobyAnVGhpcyB0ZXN0IGZ1bmN0aW9uIDIgaXMgd29ya2luZy4nO30gdGVzdDIoKTs=它只是以下内容:function test2() {echo 'This test function 2 is working.';} test2();笔记2:当我第一次尝试创建一个数据 URI 时(很久之前我在这里发布)我在百分比编码上犯了一个错误,使数据 URI 无效。我尝试对 Data URI 进行 base-64 编码(这有效),之后我没有返回到原始百分比编码的 Data URI 来找出我哪里出错了。但是,重要的是要注意(除了混淆)根本没有理由在数据 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:虽然,到目前为止,我还没有成功使用includeData URI while allow_url_includeremains set to FALSE,但我想我会比较一下:编写一个字符串并eval()在该字符串上运行以执行它创建文件并include在该文件上运行测试包括运行其中一个进程超过 10,000 次。我没有运行一次单个测试,而是连续运行了一组 6 个测试。总的来说,我跑了 5 组。这就涉及到 10,000 个流程的 5 组 6 次测试。综上所述:运行eval()(通常但不总是)比简单地声明和执行一个函数慢 10 多倍。创建、写入和保存文件,然后include在该文件上运行比声明和执行函数慢 1000 多倍。声明和执行一个简单函数的平均时间(来自 60,000 个样本)是 0.000002194s运行相同简单函数的平均时间eval()(来自 60,000 个样本)是 0.00009262s因此,任何花费eval()100 倍的时间来解析和执行的字符串,仍然只需要不到百分之一秒( < 0.01s) 的时间即可完成。
查看完整描述

3 回答

?
呼如林

TA贡献1798条经验 获得超3个赞

我想到的一个想法是将生成的代码保存在一个临时文件中(在tmpfs中),然后include对其进行处理。


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


file_put_contents("/mnt/tempfs/<name>.php", "<?php\n$Test_Function");


include "/mnt/tempfs/<name>.php";

关于性能:


tmpfs 工具允许创建内容驻留在虚拟内存中的文件系统。由于此类文件系统上的文件通常驻留在 RAM 中,因此文件访问速度非常快。


查看完整回答
反对 回复 2023-04-28
?
慕码人2483693

TA贡献1860条经验 获得超9个赞

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

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

  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 ,您也可以确信正确的代码会运行


查看完整回答
反对 回复 2023-04-28
?
一只甜甜圈

TA贡献1836条经验 获得超5个赞

您可以尝试编写您的data:php://memory。因为它在那里就像普通文件一样,你可以包括它吗?



查看完整回答
反对 回复 2023-04-28
  • 3 回答
  • 0 关注
  • 108 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信