php伪协议与文件包含笔记

谈到php伪协议,那么肯定要从两个php.ini中的配置项说起。

allow_url_fopen

文档摘抄:

allow_url_fopen boolean

本选项激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象例如文件。默认的封装协议提供用 ftp 和 http 协议来访问远程文件,一些扩展库例如 zlib 可能会注册更多的封装协议。

从文档得出一些信息:

allow_url_fopen选项是为了配置远程对象访问的权限,一切本地对象的访问权限其实不受这个配置选项的影响。本地是相对于服务器来说的,对服务器来说服务器的文件是属于本地文件。所以此选项影响的是RFI,即远程文件包含。包括HTTP(S)协议和FTP协议。

对于data://协议来说,官方文档说不受allow_url_fopen影响,但实践发现,其实是需要allow_url_fopen=On才生效的。

所以allow_url_fopen影响如下协议(即需要allow_url_fopen=On才生效):

1
http(s)://、ftp://、data://

allow_url_include

文档摘抄:

allow_url_include boolean

This option allows the use of URL-aware fopen wrappers with the following functions: include, include_once, require, require_once.

Note:

This setting requires allow_url_fopen to be on.

从文档可以知道:

allow_url_include 配置是为了让include, include_once, require, require_once 四个函数可以包含远程对象而设置的。并且需要allow_url_fopen=On 才可以包含远程文件。所以远程文件包含需要allow_url_fopen和allow_url_include同时为On才可以。

其实allow_url_include还约束一些其他协议:

1
2
php://input、php://stdin
php://memory、php://temp

总结:

allow_url_fopen是为了约束fopen、file_get_contents等函数访问远程对象而设置的,当include、include_once、require、require_once函数要访问远程对象时,还需要allow_url_include=On。

除此之外,通过实践发现,

只需要allow_url_include=On就生效的协议:

1
2
3
4
php://input
php://stdin
php://memory
php://temp

只需要allow_url_fopen=On就生效的协议:

1
2
http(s)://
ftp://

这里注意,当作为include类函数的参数传入时,还需要allow_url_include=On, 但对于协议本身来说并不需要allow_url_include=On

需要allow_url_fopen=On、allow_url_include=On同时才生效的协议:

1
data://

不依赖于两者的协议:

1
2
3
4
php://filter等
file://
zlib://
etc.

部分验证代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* http:// https:// ftp://
allow_url_include=Off
allow_url_fopen=On
*/
echo file_get_contents('http://your_host/test.html'); // Can read properly.
include('http://your_host/test.html'); // Error: Need allow_url_include to be on.
/* php://input
allow_url_include=On
allow_url_fopen=Off
*/
echo file_get_contents('php://input');
include('php://input');
/* data://
allow_url_include=On
allow_url_fopen=On
*/
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');
include('data://text/plain;base64,SSBsb3ZlIFBIUAo=');