PHP 伪协议:使用 php://filter 为数据流应用过滤器

文章目录

  • 参考
  • 环境
  • 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

参考

项目 描述
搜索引擎 BingGoogle
AI 大模型 文心一言通义千问讯飞星火认知大模型ChatGPT
PHP 官方 filesystem.configuration.php
PHP 官方 PHP Manual
PHP 官方 wrappers.php.php
PHP 官方 filters.php

环境

项目 描述
PHP 5.5.05.6.87.0.07.2.57.4.98.0.08.2.9
PHP 编辑器 PhpStorm 2023.1.1(专业版)

PHP 伪协议

概念

在 PHP 中,伪协议(Pseudo Protocols) 也被称为 流包装器,这些伪协议以 php:// 开头,后面跟着一些参数,用于指定 要执行的操作需要访问的资源
伪协议表明这些协议并不是一个 真实的外部协议,例如 httpftp。PHP 伪协议的出现是为了提供一个 统一的简洁的 接口来处理 不同的数据流。这些伪协议可以被看作是一种 桥梁,它们允许开发者 使用常规的文件操作函数来处理各种不同的数据流

为什么需要 PHP 伪协议?

PHP 伪协议:使用 php://filter 为数据流应用过滤器_第1张图片

PHP 所提供的伪协议带来的优点整理如下:

项目 描述
一致性 开发者 不必为不同的数据流编写特定的处理代码。通过使用伪协议,开发者能够使用 熟悉的标准的 文件操作函数来处理各种数据。
灵活性 伪协议提供了 多种不同的方式来访问和处理数据例如,您可以使用 php://stdinphp://stdout 来处理标准输入和标准输出;使用 php://memoryphp://temp 来在内存中或临时文件中处理数据。这种灵活性允许开发人员根据具体需求选择最合适的方式来处理数据。
优化工作流 php://input 伪协议可以 直接读取原始的 POST 数据,而不需要依赖特定的全局变量,这样可以在不同的上下文中更灵活地处理输入数据;php://tempphp://memory 伪协议允许在内存中 创建临时数据流,而 无需实际文件存储。这对于处理临时数据或进行中间数据处理非常有用,而 不会产生硬盘 I/O开销。

php://filter

概念

php://filter 的主要作用是提供一种机制,让您可以轻松地 在数据流上应用一个或多个过滤器

PHP 伪协议:使用 php://filter 为数据流应用过滤器_第2张图片

格式

php://filter 的基本格式如下:

php://filter/read=?/resource=?

其中:

项目 描述
resource php://filter 中,resource 参数是必须的。resource 用于指定 需要进行筛选过滤的数据流
read read 参数指定 一个或多个过滤器 用于 操作,多个过滤器之间以管道符 | 进行分隔。
write write 参数指定 一个或多个过滤器 用于 操作,多个过滤器之间以管道符 | 进行分隔。

注:

任何没有以 read=write= 作前缀的筛选器列表会 视情况应用于读或写操作。这意味着在指定筛选器的过程中,readwrite 参数可被省略。

基本使用

普通读写

file_get_contents 与 file_put_contents

在使用 php://filter 伪协议的过程中,省略 read=write=过滤器列表 将能够实现对数据的 普通(不使用过滤器) 读写操作。




# 省略过滤器列表实现文本的普通读写操作

# 通过 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

includerequire 等函数也可用于处理包含 PHP 伪协议的字符串,只不过 include 等函数仅能使用 php://filter 进行读取操作,且 被读取的数据将被包含至当前 PHP 上下文中,作为 PHP 代码进行执行。对此,请参考如下示例:

content.txt 文件中的内容




var_dump('Hello World');

示例代码




# 尝试通过 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-encodeconvert.base64-decodephp://filter 所支持的过滤器,使用这两个过滤器等同于使用 base64_encode()base64_decode() 对数据流进行处理。

PHP 伪协议:使用 php://filter 为数据流应用过滤器_第3张图片

举个栗子




# 省略 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 加密,将获得加密文本的原始文本

PHP 伪协议:使用 php://filter 为数据流应用过滤器_第4张图片

这种加密方法的主要优点是它的简单性和对称性,但显然,ROT13 不提供真正的安全性,因为它很容易破解。事实上,ROT13 经常在在线社区中用作一种简单的方式来 隐藏剧透、答案或轻微的冒犯内容,而不是用作真正的加密手段

string.rot13

通过 php://filter 使用 string.rot13 过滤器即可对数据流进行 rot13 处理。对此,请参考如下示例:




# 对数据流进行 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 指定过滤器时,可以通过 管道符 | 指定多个过滤器(过滤器列表),这些过滤器将按照 从左至右 的顺序 依次 对数据流进行处理。对此,请参考如下示例:




# 依次对数据流进行 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"

注意事项

在使用 管道符 | 连接多个过滤器时,与管道符之间存在空格的过滤器均将失效。对此,请参考如下示例:




# 两个过滤器与管道符之间均存在空格,故数据将不进行任何处理存入文件 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_fopenallow_url_include 配置项,这两个配置项将决定这些函数能否成功 访问执行 来自 远程服务器 中的数据。

PHP 伪协议:使用 php://filter 为数据流应用过滤器_第5张图片

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://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 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 的 includeinclude_oncerequirerequire_once 语句。当 allow_url_include 被设置为 On 时,PHP 允许通过 URL 的形式,从远程服务器 包含和执行 PHP 文件。

注:

PHP5.2 开始,allow_url_include 由原先的默认开启转为默认关闭。如果需要使用 include 等函数处理远程文件,请尝试通过 PHP 配置文件 php.ini 或其他方式开启该配置项。

inlcude 与 php://filter

在使用 includeinclude_oncerequirerequire_once 等函数的同时使用 php://filter 伪协议对 远程文件 进行过滤操作将导致远程文件被包含至当前文件中,且 远程文件将被视为 PHP 代码 进行执行。对此,请参考如下示例:

http://192.168.1.8/target

尝试通过浏览器访问 IP 地址为 192.168.1.8 的服务器中的 target 文件,访问结果如下:

PHP 伪协议:使用 php://filter 为数据流应用过滤器_第6张图片

示例代码




# 尝试通过 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_fopenallow_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_includeallow_url_fopen 两个配置项均被开启时,allow_url_include 才能够发挥作用。若仅有 allow_url_include 配置项被开启,则无法发挥 allow_url_include 配置项所起到的功能。

你可能感兴趣的:(安全,后端,php,过滤器,PHP,伪协议,base64,rot13,过滤器列表,PHP,配置项)