文章目录
- 参考
- 环境
- PHP 伪协议
- 概念
- 为什么需要 PHP 伪协议?
- php://filter
- 概念
- 格式
- 基本使用
- 普通读写
- file_get_contents 与 file_put_contents
- include
- 过滤器的基本使用
- base64 的编码与解码
- rot13 加解密
- rot13 算法
- string.rot13
- 过滤器列表
- 多个过滤器的使用
- 注意事项
- 处理远程文件
- allow_url_fopen
- allow_url_fopen 配置项
- file_get_contents 与 php://filter
- allow_url_include
- allow_url_include 配置项
- inlcude 与 php://filter
参考
项目 | 描述 |
---|---|
搜索引擎 | Bing、Google |
AI 大模型 | 文心一言、通义千问、讯飞星火认知大模型、ChatGPT |
PHP 官方 | filesystem.configuration.php |
PHP 官方 | PHP Manual |
PHP 官方 | wrappers.php.php |
PHP 官方 | filters.php |
环境
项目 | 描述 |
---|---|
PHP | 5.5.0 、5.6.8 、7.0.0 、7.2.5 、7.4.9 、8.0.0 、8.2.9 |
PHP 编辑器 | PhpStorm 2023.1.1(专业版) |
PHP 伪协议
概念
在 PHP 中,伪协议(Pseudo Protocols)
也被称为 流包装器
,这些伪协议以 php://
开头,后面跟着一些参数,用于指定 要执行的操作
或 需要访问的资源
。
伪协议表明这些协议并不是一个 真实的外部协议
,例如 http
或 ftp
。PHP 伪协议的出现是为了提供一个 统一的
、简洁的
接口来处理 不同的数据流
。这些伪协议可以被看作是一种 桥梁
,它们允许开发者 使用常规的文件操作函数来处理各种不同的数据流
。
为什么需要 PHP 伪协议?
PHP 所提供的伪协议带来的优点整理如下:
项目 | 描述 |
---|---|
一致性 | 开发者 不必为不同的数据流编写特定的处理代码 。通过使用伪协议,开发者能够使用 熟悉的 、标准的 文件操作函数来处理各种数据。 |
灵活性 | 伪协议提供了 多种不同的方式来访问和处理数据 。例如 ,您可以使用 php://stdin 和 php://stdout 来处理标准输入和标准输出;使用 php://memory 和 php://temp 来在内存中或临时文件中处理数据。这种灵活性允许开发人员根据具体需求选择最合适的方式来处理数据。 |
优化工作流 | php://input 伪协议可以 直接读取原始的 POST 数据 ,而不需要依赖特定的全局变量,这样可以在不同的上下文中更灵活地处理输入数据;php://temp 和 php://memory 伪协议允许在内存中 创建临时数据流 ,而 无需实际文件存储 。这对于处理临时数据或进行中间数据处理非常有用,而 不会产生硬盘 I/O 开销。 |
php://filter
概念
php://filter
的主要作用是提供一种机制,让您可以轻松地 在数据流上应用一个或多个过滤器
。
格式
php://filter
的基本格式如下:
php://filter/read=?/resource=?
其中:
项目 | 描述 |
---|---|
resource | 在 php://filter 中,resource 参数是必须的。resource 用于指定 需要进行筛选过滤的数据流 。 |
read | read 参数指定 一个或多个过滤器 用于 读 操作,多个过滤器之间以管道符 | 进行分隔。 |
write | write 参数指定 一个或多个过滤器 用于 写 操作,多个过滤器之间以管道符 | 进行分隔。 |
注:
任何没有以 read=
或 write=
作前缀的筛选器列表会 视情况应用于读或写操作
。这意味着在指定筛选器的过程中,read
与 write
参数可被省略。
基本使用
普通读写
file_get_contents 与 file_put_contents
在使用 php://filter
伪协议的过程中,省略 read=
或 write=
与 过滤器列表
将能够实现对数据的 普通(不使用过滤器)
读写操作。
<?php
# 省略过滤器列表实现文本的普通读写操作
# 通过 php://filter 伪协议指定需要写入数据的文件
file_put_contents('php://filter/resource=file.txt', 'Hello World');
# 通过 php://filter 伪协议指定需要读取数据的文件
$content = file_get_contents('php://filter/resource=file.txt');
var_dump($content);
执行效果
由于在使用 php://filter
伪协议的过程中没有指定需要使用到的过滤器,PHP 抛出了 Warning
异常。
string(11) "Hello World"
PHP Warning: file_put_contents(): Unable to locate filter "resource=file.txt" in C:\index.php on line 7
PHP Warning: file_put_contents(): Unable to create filter (resource=file.txt) in C:\index.php on line 7
PHP Warning: file_get_contents(): Unable to locate filter "resource=file.txt" in C:\index.php on line 9
PHP Warning: file_get_contents(): Unable to create filter (resource=file.txt) in C:\index.php on line 9
include
include
及 require
等函数也可用于处理包含 PHP 伪协议的字符串,只不过 include
等函数仅能使用 php://filter
进行读取操作,且 被读取的数据将被包含至当前 PHP 上下文中,作为 PHP 代码进行执行
。对此,请参考如下示例:
content.txt 文件中的内容
<?php
var_dump('Hello World');
示例代码
<?php
# 尝试通过 php://filter 协议过滤本地文件中的内容
include('php://filter/resource=./content.txt');
执行效果
由于在使用 php://filter
伪协议的过程中未指定过滤器,故 PHP 抛出了 Warning
异常。
在使用 php://filter
读取 content.txt
文件后,该文件中的内容被 PHP 视为 PHP 代码进行执行。因此输出了 string(11) "Hello World"
而不是输出 content.txt
文件中的实际内容。
PHP Warning: include(): Unable to locate filter "resource=." in C:\demo.php on line 5
PHP Warning: include(): Unable to create filter (resource=.) in C:\demo.php on line 5
PHP Warning: include(): Unable to locate filter "content.txt" in C:\demo.php on line 5
PHP Warning: include(): Unable to create filter (content.txt) in C:\demo.php on line 5
string(11) "Hello World"
过滤器的基本使用
base64 的编码与解码
convert.base64-encode
与 convert.base64-decode
是 php://filter
所支持的过滤器,使用这两个过滤器等同于使用 base64_encode()
与 base64_decode()
对数据流进行处理。
举个栗子
<?php
# 省略 write=
file_put_contents('php://filter/convert.base64-encode/resource=./file.txt', 'Hello World');
# 获取 base64 编码后的内容
$content = file_get_contents('php://filter/resource=./file.txt');
var_dump($content);
# 通过 convert.base64-decode 过滤器数据流进行 base64 解码操作
$content = file_get_contents('php://filter/read=convert.base64-decode/resource=./file.txt');
var_dump($content);
执行效果
string(16) "SGVsbG8gV29ybGQ="
string(11) "Hello World"
PHP Warning: file_get_contents(): Unable to locate filter "resource=." in C:\demo.php on line 8
PHP Warning: file_get_contents(): Unable to create filter (resource=.) in C:\demo.php on line 8
PHP Warning: file_get_contents(): Unable to locate filter "file.txt" in C:\demo.php on line 8
PHP Warning: file_get_contents(): Unable to create filter (file.txt) in C:\demo.php on line 8
rot13 加解密
rot13 算法
ROT13(Rotate By 13 Places)是一种简单的 字母替代密
码,是 凯撒密码的一种变体
。其基本思想是将字母表中的每一个字母移动 13
个位置。因为拉丁字母表有 26
个字母,所以 ROT13 解密
是其自身的 逆运算:即对一个已经 ROT13 加密的文本再次进行 ROT13 加密,将获得加密文本的原始文本
。
这种加密方法的主要优点是它的简单性和对称性,但显然,ROT13
不提供真正的安全性,因为它很容易破解。事实上,ROT13 经常在在线社区中用作一种简单的方式来 隐藏剧透、答案或轻微的冒犯内容,而不是用作真正的加密手段
。
string.rot13
通过 php://filter
使用 string.rot13
过滤器即可对数据流进行 rot13
处理。对此,请参考如下示例:
<?php
# 对数据流进行 ROT13 加密
file_put_contents('php://filter/write=string.rot13/resource=file.txt', 'Hello World');
# 读取数据但不对数据流应用任何过滤器
include('php://filter/resource=./file.txt');
print("\n");
# 对数据流进行 ROT13 加密以获取其原文
$content = file_get_contents('php://filter/read=string.rot13/resource=file.txt');
var_dump($content);
执行效果
Uryyb Jbeyq
string(11) "Hello World"
PHP Warning: include(): Unable to locate filter "resource=." in C:\demo.php on line 8
PHP Warning: include(): Unable to create filter (resource=.) in C:\demo.php on line 8
PHP Warning: include(): Unable to locate filter "file.txt" in C:\demo.php on line 8
PHP Warning: include(): Unable to create filter (file.txt) in C:\demo.php on line 8
过滤器列表
多个过滤器的使用
在为 php://filter
指定过滤器时,可以通过 管道符 |
指定多个过滤器(过滤器列表),这些过滤器将按照 从左至右
的顺序 依次
对数据流进行处理。对此,请参考如下示例:
<?php
# 依次对数据流进行 base64 编码处理,rot13 处理。
file_put_contents('php://filter/convert.base64-encode|string.rot13/resource=file.txt', 'Hello World');
# 对 file.txt 文件中的内容进行普通读取
$raw_content = file_get_contents('./file.txt');
var_dump($raw_content);
# 由于没有先将文件中的内容进行 rot13 处理,直接对其进行解码将无法恢复原数据内容。
var_dump(base64_decode($raw_content));
# 先对文件中的内容进行 rot13 处理,再对处理结果进行 base64 解码
$content = file_get_contents('php://filter/string.rot13|convert.base64-decode/resource=./file.txt');
var_dump($content);
执行效果
string(16) "FTIfoT8tI29loTD="
string(11) "2�?-#oe�0"
string(11) "Hello World"
注意事项
在使用 管道符 |
连接多个过滤器时,与管道符之间存在空格的过滤器均将失效
。对此,请参考如下示例:
<?php
# 两个过滤器与管道符之间均存在空格,故数据将不进行任何处理存入文件 file.txt 中。
file_put_contents('php://filter/convert.base64-encode | string.rot13/resource=file.txt', 'Hello World');
# 对 file.txt 文件中的内容进行普通读取
$raw_content = file_get_contents('./file.txt');
var_dump($raw_content);
# 由于 string.rot13 与过滤器之间存在空格,
# 故仅有 convert.base64-decode 过滤器生效。
$content = file_get_contents('php://filter/string.rot13 |convert.base64-decode/resource=./file.txt');
var_dump($content === base64_decode($raw_content));
# 由于 convert.base64-decode 与过滤器之间存在空格,
# 故仅有 string.rot13 过滤器生效。
$content = file_get_contents('php://filter/string.rot13| convert.base64-decode/resource=./file.txt');
var_dump($content === str_rot13($raw_content));
执行效果
由于管道符 |
与过滤器之间存在空格导致部分过滤器无法正常使用,PHP 抛出 Warning
异常信息尝试对此进行提示。
PHP Warning: file_put_contents(): Unable to create or locate filter "convert.base64-encode " in C:\demo.php on line 5
PHP Warning: file_put_contents(): Unable to create filter (convert.base64-encode ) in C:\demo.php on line 5
PHP Warning: file_put_contents(): Unable to locate filter " string.rot13" in C:\demo.php on line 5
PHP Warning: file_put_contents(): Unable to create filter ( string.rot13) in C:\demo.php on line 5
PHP Warning: file_get_contents(): Unable to locate filter "string.rot13 " in C:\demo.php on line 13
PHP Warning: file_get_contents(): Unable to create filter (string.rot13 ) in C:\demo.php on line 13
PHP Warning: file_get_contents(): Unable to locate filter " convert.base64-decode" in C:\demo.php on line 18
PHP Warning: file_get_contents(): Unable to create filter ( convert.base64-decode) in C:\demo.php on line 18
string(11) "Hello World"
bool(true)
bool(true)
注:
实际上,指定过滤器的过程中出现 不必要的空格
而导致过滤器失效的情况并不仅仅存在上面一种。建议在使用 php://filter
为数据流应用过滤器时,不要出现不必要的空格,防止未预料的事情发生。
处理远程文件
您可以在 PHP 支持使用 PHP 伪协议的函数中使用 php://filter
过滤数据流。但如果需要过滤的数据来自于 远程服务器中
,则需要重点关注 allow_url_fopen
或 allow_url_include
配置项,这两个配置项将决定这些函数能否成功 访问
或 执行
来自 远程服务器
中的数据。
allow_url_fopen
allow_url_fopen 配置项
allow_url_fopen
是 PHP 中的一个配置选项,它决定了 PHP 是否能够通过 URL (而非本地文件路径)
来打开文件。这个配置选项的值会影响到一些 PHP 中与文件操作相关的函数的行为,例如 fopen()
和 file_get_contents()
。具体来说,当 allow_url_fopen
被设置为 On
(开启)时,这些函数可以用来 读取
或 写入远程文件
。而当该配置项被设置为 Off
(关闭)时,这些函数 只能用于操作本地文件
。
file_get_contents 与 php://filter
在尝试使用 file_get_contents
函数获取 远程文件
中的内容时,请确保 PHP 已经开启了 allow_url_fopen
配置项,否则 PHP 将抛出 Warning
异常。对此,请参考如下示例:
<?php
# 尝试通过 php://filter 对远程文件进行普通读取
$result = file_get_contents('php://filter/resource=http://192.168.1.8t/target');
var_dump($result);
执行效果
PHP Warning: file_get_contents(php://filter/resource=http://192.168.1.8/target): Failed to open stream: operation failed in C:\demo.php on line 5
bool(false)
目前,allow_url_fopen
在 PHP 各版本中默认情况下均是开启的。若未开启该选项,请尝试通过 PHP 配置文件 php.ini
文件对该配置进行开启。开启该配置后,执行上述示例代码将得到下述结果:
string(33) "<?php
var_dump('Hello World');
"
PHP Warning: file_get_contents(): Unable to locate filter "resource=http:" in C:\demo.php on line 5
PHP Warning: file_get_contents(): Unable to create filter (resource=http:) in C:\demo.php on line 5
PHP Warning: file_get_contents(): Unable to locate filter "192.168.1.8" in C:\demo.php on line 5
PHP Warning: file_get_contents(): Unable to create filter (192.168.1.8) in C:\demo.php on line 5
PHP Warning: file_get_contents(): Unable to locate filter "target" in C:\demo.php on line 5
PHP Warning: file_get_contents(): Unable to create filter (target) in C:\demo.php on line 5
allow_url_include
allow_url_include 配置项
allow_url_include
是 PHP 的一个配置指令,与 allow_url_fopen
类似,但 allow_url_include
配置专门针对 PHP 的 include
、include_once
、 require
及 require_once
语句。当 allow_url_include
被设置为 On
时,PHP 允许通过 URL 的形式,从远程服务器 包含和执行
PHP 文件。
注:
自 PHP5.2
开始,allow_url_include
由原先的默认开启转为默认关闭。如果需要使用 include
等函数处理远程文件,请尝试通过 PHP 配置文件 php.ini
或其他方式开启该配置项。
inlcude 与 php://filter
在使用 include
、include_once
、require
及 require_once
等函数的同时使用 php://filter
伪协议对 远程文件
进行过滤操作将导致远程文件被包含至当前文件中,且 远程文件将被视为 PHP 代码
进行执行。对此,请参考如下示例:
http://192.168.1.8/target
尝试通过浏览器访问 IP 地址为 192.168.1.8
的服务器中的 target
文件,访问结果如下:
示例代码
<?php
# 尝试通过 php://filter 协议过滤远程文件中的内容
include('php://filter/resource=http://192.168.1.8/target');
执行效果
由于未指定用于处理数据流的过滤器,PHP 抛出了多个 Warning
异常。
由上述示例的执行结果来看,php://filter
协议成功包含并执行了远程文件中的内容,因此输出了 string(11) "Hello World"
。
PHP Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0
string(11) "Hello World"
PHP Warning: include(): Unable to locate filter "resource=http:" in C:\demo.php on line 5
PHP Warning: include(): Unable to create filter (resource=http:) in C:\demo.php on line 5
PHP Warning: include(): Unable to locate filter "192.168.1.8" in C:\demo.php on line 5
PHP Warning: include(): Unable to create filter (192.168.1.8) in C:\demo.php on line 5
PHP Warning: include(): Unable to locate filter "target" in C:\demo.php on line 5
PHP Warning: include(): Unable to create filter (target) in C:\demo.php on line 5
若执行上述代码前,allow_url_fopen
与 allow_url_include
配置项未被开启,则示例代码的执行结果将为如下内容:
PHP Warning: include(php://filter/resource=http://192.168.1.8/target): Failed to open stream: operation failed in C:\demo.php on line 5
PHP Warning: include(): Failed opening 'php://filter/resource=http://192.168.1.8/target' for inclusion (include_path='.;C:\php\pear') in C:\demo.php on line 5
注:
allow_url_include
的生效依赖于 allow_url_fopen
配置项的开启。具体而言,当 allow_url_include
与 allow_url_fopen
两个配置项均被开启时,allow_url_include
才能够发挥作用。若仅有 allow_url_include
配置项被开启,则无法发挥 allow_url_include
配置项所起到的功能。