php - 如何让 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 访问include
URI 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
我想我会比较:
- 编写一个字符串并
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
) 即可完成。
所以......如果我找不到任何方法到include
Data URI,我会继续eval()
,尽管它比 PHP 的正常速度慢 10 倍以上,但它仍然非常棒。
这里(为了完整起见)是注释 3中测试的结果(以秒为单位) :
设置 1
PHP 脚本(控制)(x 10000)
- 0.0083s
- 0.0079s
- 0.0080s
- 0.0082s
- 0.0082s
- 0.0088s
- 0.0082s (平均)
评估测试 (x 10000)
- 0.0881s
- 0.0928s
- 0.0996s
- 0.1023s
- 0.1420s
- 0.1244s
- 0.1082s (平均)
创建文件测试 (x 10000)
- 12.8188s
- 15.3593s
- 17.8400s
- 13.7488s
- 28.2703s
- 26.3024s
- 19.0566s (平均)
设置 2
PHP 脚本(控制)(x 10000)
- 0.0137s
- 0.0135s
- 0.0124s
- 0.0161s
- 0.0130s
- 0.0133s
- 0.0137s (平均)
评估测试 (x 10000)
- 0.1361s
- 0.1347s
- 0.0947s
- 0.0909s
- 0.1067s
- 0.1839s
- 0.1245s (平均)
创建文件测试 (x 10000)
- 10.9942s
- 13.8902s
- 19.4267s
- 29.3531s
- 24.4923s
- 21.0390 秒
- 19.8659s (平均)
设置 3
PHP 脚本(控制)(x 10000)
- 0.0147s
- 0.0189s
- 0.0178s
- 0.0164s
- 0.0165s
- 0.0180s
- 0.0171s (平均)
评估测试 (x 10000)
- 0.1436s
- 0.1421s
- 0.1675s
- 0.1977s
- 0.2570s
- 0.2749s
- 0.1971s (平均)
创建文件测试 (x 10000)
- 17.8473s
- 13.0513s
- 12.8365s
- 17.9243s
- 21.5655s
- 24.1693s
- 17.8990 秒 (平均)
设置 4
PHP 脚本(控制)(x 10000)
- 0.0195s
- 0.0207s
- 0.0222s
- 0.0192s
- 0.0200s
- 0.0204s
- 0.0203s (平均)
评估测试 (x 10000)
- 0.2109s
- 0.2040s
- 0.2061s
- 0.1895s
- 0.2305s
- 0.2318s
- 0.2121s (平均)
创建文件测试 (x 10000)
- 20.9385s
- 17.7350s
- 27.2538s
- 21.9894s
- 27.6480 秒
- 20.2680s
- 22.6388s (平均)
设置 5
PHP 脚本(控制)(x 10000)
- 0.0259s
- 0.0609s
- 0.0556s
- 0.0695s
- 0.0498s
- 0.0406s
- 0.0504s (平均)
评估测试 (x 10000)
- 0.4123s
- 0.3235s
- 0.2588s
- 0.2810s
- 0.2172s
- 0.2132s
- 0.2843s (平均)
创建文件测试 (x 10000)
- 14.9768s
- 13.9306s
- 26.4742s
- 26.1288s
- 35.6069s
- 23.1933s
- 23.3851s (平均)
解决方案
我闻到了X/Y 问题- 您已经将URI 视为特定问题的可能解决方案,eval
并data:
发现了两者的缺点,但没有回到最初的问题陈述。
从各种评论中,我们可以将问题定义为找到一种机制:
- 可以执行任意动态代码
- 具有与正常类似的解析性能
include
- 将允许代码缓存在 OpCache 中
- 不需要在其他地方放松安全措施
您说eval
在第 2 点(因为它启动了一个新的解析器)和第 3 点(因为没有可供 OpCache 使用的密钥)失败。
您已经确定data:
URI 在第 4 点失败(因为它们需要allow_url_include
),但我怀疑它们也在第 2 点失败(因为 base64 解码是不小的开销)和第 3 点(因为 OpCache 不支持缓存任意流;我相信它只支持文件路径和 PHAR 内容)
正如另一个答案中所建议的那样,更好的整体解决方案是将代码写入“真实”文件,使用内存中的文件系统tmpfs
来提高性能。这符合上述所有四个标准,因为就 PHP 而言,它与任何其他源文件相同;唯一的额外开销是将文件写入虚拟文件系统的 I/O 速度。
关键的优化将确保您跟踪动态内容何时发生更改,从某种修订 ID 或字符串的哈希生成唯一的文件路径。这可以实现两件事:
- 您可以跳过写入磁盘上已存在的文件,从而节省 I/O 时间
- OpCache 将能够缓存此文件的内容并重用它,即使您设置
opcache.validate_timestamps
为 false ,您也可以确信运行正确的代码
推荐阅读
- reactjs - React-Bootstrap 中的 LinkContainer 问题
- mysql - 在mysql中使用外键将数据从一个表复制到另一个表
- vue.js - Nuxt 松散 htmlAttr 后显示另一个页面组件
- python - 如何更新字典列表中的每个字典
- timeout - 停止 API 之一时出现负载均衡器 503 错误
- azure - Azure Kubernetes 节点端口
- java - Spring Boot 构建良好,但在应用程序即将启动时抛出此错误。有没有人见过这样的东西?
- javascript - 单击按钮后消失的加载屏幕不起作用
- testing - 在 Shopware 6.3.5.2 的生产模板中运行所有测试
- databricks - 如何防止在 Azure Databricks 中删除 Delta Lake 检查点?