PHP Wrappers

分类

  • file:// — 访问本地文件系统
  • http:// — 访问 HTTP(s) 网址
  • ftp:// — 访问 FTP(s) URLs
  • php:// — 访问各个输入/输出流(I/O streams)
  • zlib:// — 压缩流
  • data:// — 数据(RFC 2397)
  • glob:// — 查找匹配的文件路径模式
  • phar:// — PHP 归档
  • ssh2:// — Secure Shell 2
  • rar:// — RAR
  • ogg:// — 音频流
  • expect:// — 处理交互式的流

php://

直接访问PHP进程的输入或者输出流:

  • php://stdin:只读
  • php://stdout:只写
  • php://stderr:只写

php://input

php://input是可以访问请求的原始数据的只读流。

POST请求的情况下,最好使用php://input来替代$_HTTP_RAW_POST_DATA,因为它不依赖于特定的php.ini指令。

enctype="multipart/form-data"的时候php://input是无效的。

php://output

php://output是一个只写的数据流,允许你以printecho一样的方式写入到输出缓冲区。

php://fd

php://fd允许直接访问指定的文件描述符,如php://fd/3引用了文件描述符3。

php://memory和php://temp

php://memory总是把数据存储在内存中

php://temp会在内存量达到预定义的限制之后(默认2M)存入临时文件中。临时文件位置的决定和sys_get_temp_dir()的方式一致。

php://filter

php://filter是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式的文件函数非常有用,类似readfile()file()file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。

php://filter目标使用一下的参数作为它路径的一部分。

名称 描述
resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表> 可选参数。可以设定一个或多个过滤器名称,以管道符/分隔。
write=<写链的筛选列表> 可选参数。可以设定一个或多个过滤器名称,以管道符/分隔。
<; 两个链的筛选列表> 任何没有以read=write=作为前缀的筛选器列表会视情况应用于读链或写链。

封装协议摘要(针对php://filter,参考被筛选的封装器

属性 支持
受限于allow_url_fopen No
受限于allow_url_include php://inputphp://stdinphp://memoryphp://temp
允许读取 php://stdinphp://inputphp://fdphp://memoryphp://temp
允许写入 php://stdoutphp://stderrphp://outputphp://fdphp://memoryphp://temp
允许追加 同写入
允许同时读写 php://fdphp://memoryphp://temp
支持stat() php://memoryphp://temp
支持unlink() No
支持rename() No
支持mkdir() No
支持rmdir() No
仅支持stream_select() php://stdinphp://stdoutphp://stderrphp://fdphp://temp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
/* 等同于
readfile("http://www.a.com");
实际上没有指定过滤器
*/
readfile("php://filter/resource=http://www.a.com");


/*
采用给一个或
以管道符 | 分隔的
多个过滤器
*/
/*以大写字母输入*/
readfile("php://filter/read=string.toupper/resource=http://www.a.com");

/*转换成大写,再使用ROT13加密之后*/
readfile("php://filter/read=string.toupper|string.rot13/resource=http://www.a.com");

/*关于管道顺序,总是从做往右做处理*/
/*
base64_encode(test.txt) => test2.txt
*/
echo "string -> toupper -> base64-encode: ";
echo readfile("php://filter/read=string.toupper|convert.base64-encode/resource=test.txt");

echo "<br>";

echo "reversed process: ";
echo readfile("php://filter/read=convert.base64-decode|string.tolower/resource=test2.txt");


/*
通过 rot13 过滤器筛选出字符 "Hello World"
然后写入当前目录下的 test.txt
*/
file_put_contents("php://filter/write=string.rot13/resource=test.txt", "Hello World");
?>

可用过滤器列表

It is worth noting a slight asymmetry between stream_filter_append() and stream_filter_prepend(). Every PHP stream contains a small read buffer where it stores blocks of data retrieved from the filesystem or other resource in order to process data in the most efficient manner. As soon as data is pulled from the resource into the stream’s internal buffer, it is immediately processed through any attached filters whether the PHP application is actually ready for the data or not. If data is sitting in the read buffer when a filter is appended, this data will be immediately processed through that filter making the fact that it was sitting in the buffer seem transparent. However, if data is sitting in the read buffer when a filter is prepended, this data will NOT be processed through that filter. It will instead wait until the next block of data is retrieved from the resource.

字符串过滤器

  • string.rot13:使用rot13对流数据进行加密,相当于str_rot13()函数。
1
2
3
4
5
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.rot13');
fwrite($fp, "This is a test");
?>
  • string.toupper:大写
  • string.tolower:小写
  • string.strip_tags:参考strip_tags()函数

转换过滤器

具体用法参考对应函数名。

  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.*
1
2
3
4
5
6
7
8
9
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.iconv.utf-16le.utf-8');
fwrite($fp, "T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0.\0\n\0");
fclose($fp);
/*
Outputs: This is a test.
*/
?>

压缩过滤器

压缩过滤器不产生命令行工具如gzip的头和尾信息。知识压缩和解压数据流中的有效载荷部分。

  • zlib.deflatezlib.inflate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
$params = array('level' => 6, 'window' => 15, 'memory' => 9);

$original_text = "This is a test.\nThis is only a test.\nThis is not an important string.\n";
echo "The original text is " . strlen($original_text) . " characters long.\n";

$fp = fopen('test.deflated', 'w');
stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, $params);
fwrite($fp, $original_text);
fclose($fp);

echo "The compress file is " . filesize('test.deflated') . " bytes long.\n";
echo "The original text was:\n";
readfile('php://filter/zlib.inflate/resource=test.deflated');

/* Generates output:

The original text is 70 characters long.
The compressed file is 56 bytes long.
The original text was:
This is a test.
This is only a test.
This is not an important string.

*/


/*
To read a gzip encoded stream from http
*/
$opts = [
"http" => [
"method" => "GET",
"header" => ["Accept-Encoding: gzip"],
]
];
$ctx = stream_context_create($opts);

$f = fopen('http://php.net', 'r', false, $ctx);
//check stream_get_meta_data($f)["wrapper_data"] has "Content-Encoding: gzip"
stream_filter_append($f, "zlib.inflate", STREAM_FILTER_READ, ["window" => 30]);
echo stream_get_contents($f); // any stream processing
fclose($f);
?>
  • bzip2.compressbzip2.decompress

加密过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
//用 3DES 将文件加密输出
$passphrase = 'My secret';

/* Turn a human readable passphrase
* into a reproducable iv/key pair
*/
$iv = substr(md5('iv'.$passphrase, true), 0, 8);
$key = substr(md5('pass1'.$passphrase, true) .
md5('pass2'.$passphrase, true), 0, 24);
$opts = array('iv'=>$iv, 'key'=>$key);

$fp = fopen('secert-file.enc', 'wb');
stream_filter_append($fp, 'mcrypt.tripledes', STREAM_FILTER_WRITE, $opts);
fwrite($fp, 'Secret secret secret data');
fclose($fp);


//读取加密的文件
$passphrase = 'My secret';

/* Turn a human readable passphrase
* into a reproducable iv/key pair
*/
$iv = substr(md5('iv'.$passphrase, true), 0, 8);
$key = substr(md5('pass1'.$passphrase, true) .
md5('pass2'.$passphrase, true), 0, 24);
$opts = array('iv'=>$iv, 'key'=>$key);

$fp = fopen('secert-file.enc', 'rb');
stream_filter_append($fp, 'mdecrypt.tripledes', STREAM_FILTER_WRITE, $opts);
$data = rtrim(stream_get_contents($fp));
fclose($fp);

echo $data;
?>