PHP伪协议安全风险深度解析:从文件读取到代码执行

发布时间:2026/6/24 7:45:58
PHP伪协议安全风险深度解析:从文件读取到代码执行 1. 项目概述PHP伪协议与安全风险全景在Web安全领域PHP伪协议是一个既强大又危险的存在。它像一把瑞士军刀为开发者提供了灵活的文件处理和数据流操作能力但同时也为攻击者打开了一扇通往服务器内部的大门。我处理过不少安全事件其中相当一部分的初始入口点都或多或少与伪协议的不当使用或过滤不严有关。简单来说PHP伪协议允许我们以类似访问网络URL如http://的方式去访问本地文件、处理压缩包、执行编码转换等。对于开发者这是便利对于攻击者这就是武器。这篇文章我将从一个安全从业者的角度深入拆解PHP伪协议的核心机制、常见利用手法以及背后的漏洞原理。无论你是刚入门的安全爱好者还是希望加固自己应用的开发人员理解这些内容都至关重要。我们会绕过那些枯燥的官方文档式说明直接切入实战中会遇到的问题和防御思路。你会发现很多高危漏洞的根源往往是对这些“特性”的一知半解。2. PHP伪协议核心机制深度解析2.1 伪协议是什么不仅仅是文件读取很多人一提到PHP伪协议第一反应就是php://filter读文件。这固然是经典利用但理解过于片面。伪协议Wrapper在PHP中本质上是为各种I/O流操作提供的一套统一接口。它抽象了数据源无论是本地文件、网络资源、内存数据还是标准输入输出的差异让开发者可以用fopen()、file_get_contents()等函数以相同的方式去操作它们。PHP内置了多种伪协议每种都有其特定前缀和用途file:// 访问本地文件系统是默认协议。http:///https:// 访问网络资源。ftp:// 访问FTP服务器。php:// 访问各输入/输出流如标准输入、输出、错误流以及过滤器。zlib:///bzip2:///zip:// 处理压缩流。data:// 数据流允许在URL中直接嵌入数据。glob:// 查找匹配模式的文件路径。phar:// PHP归档文件流这是引发漏洞的重灾区。这些协议通过allow_url_fopen和allow_url_include这两个核心的PHP配置项进行管控。前者允许类似fopen()的函数打开URL如http、ftp后者则允许include、require等文件包含函数直接包含URL。在安全配置中allow_url_include通常被强烈建议关闭。2.2 关键协议实战拆解与利用场景2.2.1 php://filter编码与转换的利器与陷阱php://filter恐怕是曝光率最高的伪协议。它本身是一个元封装器设计用于在数据流打开时应用过滤器。这功能本用于数据转换但在攻击者手中它变成了文件读取、源码泄露的利器。其基本格式为php://filter/read过滤器链/resource目标资源。过滤器链可以串联多个过滤器。经典利用1读取PHP源码为什么include()一个PHP文件我们看到的是执行后的HTML而php://filter却能读到源码关键在于convert.base64-encode过滤器。当include或file_get_contents尝试去“执行”一个经过base64编码的流时PHP解释器会先解码。但如果我们直接读取这个编码后的流内容拿到手的就是源码的base64字符串解码即可。// 攻击载荷示例 $payload php://filter/readconvert.base64-encode/resource/var/www/html/config.php; $source file_get_contents($payload); echo base64_decode($source); // 输出PHP配置文件源码这里read指定了读取时应用的过滤器链。resource指向要读取的文件。这个技巧绕过了PHP文件被直接访问时会被解析执行的问题因为过滤器在数据流被“使用”之前就进行了编码操作。经典利用2字符串处理与死亡绕坑string.*系列的过滤器如string.rot13、string.toupper可用于简单的字符串转换。在一些非常特殊的代码审计场景下如果存在对输入内容的检查但检查逻辑有缺陷攻击者可能通过多重编码或转换来绕过检查。例如某些写死的检查可能会剔除?php标签但如果你用string.rot13处理一下标签变成了?cuc可能就绕过了检查后续再被还原执行。不过这种场景比较罕见更需要关注的是convert.iconv.*过滤器它用于字符集转换在某些特定字符集转换过程中可能会意外产生可被解析的PHP代码片段这属于更深层次的技巧。实操心得在审计代码时凡是看到file_get_contents()、fopen()、include()/require()尤其是后两者的参数是用户可控或间接可控的就要立刻警惕伪协议注入的可能。即使代码里做了../目录遍历的过滤伪协议也可能提供一个全新的攻击路径。2.2.2 php://input直接操控原始POST数据流php://input是一个只读流用于访问请求的原始数据即HTTP POST请求的body部分。当enctype不是multipart/form-data时它可以读取到未解析的POST数据。这个协议的危险性直接与allow_url_include配置挂钩。利用场景代码执行如果目标服务器开启了allow_url_include并且存在一个文件包含点用户输入被直接拼接到包含路径中那么攻击者可以这样利用正常请求包含点?filesomepage.php构造恶意请求?filephp://input在POST Body中直接写入PHP代码?php system(id); ?此时include(“php://input”)就会去执行POST Body里的PHP代码导致远程代码执行RCE。利用场景数据伪造与SSRF即使不允许包含php://input也常被用于服务端请求伪造SSRF攻击中。有些应用会使用file_get_contents()去读取用户提供的URL内容。如果用户传入php://input那么该函数会去读取本次HTTP请求的Body内容。攻击者可以借此“伪造”一个HTTP响应体欺骗后端处理逻辑。例如某个应用会读取指定URL的JSON配置攻击者就可以通过php://input直接注入恶意JSON数据。注意事项php://input的数据只能读取一次。读取后流指针就到了末尾再次读取将为空。这在编写利用工具或测试时需要注意。另外当请求类型是multipart/form-data时php://input是无效的因为PHP已经将数据解析到$_POST和$_FILES中了。2.2.3 data://内联数据协议data://协议允许在URL中直接嵌入数据格式为data://[mediatype][;base64],data。它同样受allow_url_include和allow_url_fopen控制。利用场景直接代码执行这是最直观的RCE利用方式之一。// 假设存在漏洞代码include($_GET[page] . .php); // 攻击者传入?pagedata://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOz8%2B // Base64解码后?php system(id);?当allow_url_includeOn时include(“data://text/plain;base64,PD9waHA….”)就会执行嵌入的PHP代码。即使不加;base64直接写data://text/plain,?php phpinfo();?也可能成功但需要注意代码中的特殊字符如??可能需要URL编码。利用场景文件包含与日志污染在无法直接执行代码但可以包含文件的情况下data://可以用于包含一个“虚拟”的文件内容。更常见的结合是利用日志文件包含。如果应用写日志时未过滤攻击者可以将PHP代码写入User-Agent等HTTP头然后通过file://或php://filter包含日志文件。而data://提供了一种更直接的内联方式但前提是包含函数支持该协议。2.2.4 phar://PHP归档文件的“反序列化炸弹”phar://协议用于访问PHARPHP Archive文件中的内容。PHAR是PHP的打包格式类似于JAR。其危险性主要来自于PHAR文件元数据metadata的反序列化操作。漏洞原理当使用phar://协议去访问一个文件甚至不必是.phar后缀可以是.jpg、.txt等任何文件只要文件内容符合PHAR格式时PHP会自动解析该文件的stub和manifest。在解析manifest的metadata部分时会自动触发反序列化。这意味着如果攻击者能上传一个精心构造的、包含恶意序列化数据的文件如图片并且后续有文件操作函数如file_exists()、fopen()、file_get_contents()、include()等的参数用户可控且使用了phar://协议或通过其他技巧触发协议就能触发反序列化漏洞执行任意代码。利用条件与步骤可控文件上传能上传一个文件到服务器且知道其绝对或相对路径。文件操作函数参数可控存在一个参数如$_GET[‘file’]能传入类似phar:///path/to/uploaded/image.jpg的路径。存在魔法方法反序列化触发的类中需要包含如__destruct()、__wakeup()等能在反序列化时自动执行的魔法方法并在其中存在危险操作。构造恶意PHAR文件的示例代码// exploit.php - 用于生成恶意PHAR文件 class EvilClass { public $cmd whoami; public function __destruct() { system($this-cmd); } } $phar new Phar(exploit.phar); $phar-startBuffering(); $phar-addFromString(test.txt, test); // 添加一个文件内容 $phar-setStub(?php __HALT_COMPILER(); ?); // 必须的stub $object new EvilClass(); $phar-setMetadata($object); // 将恶意对象存入metadata $phar-stopBuffering(); // 生成后将exploit.phar重命名为exploit.jpg上传攻击者上传exploit.jpg后访问存在漏洞的页面?filephar:///var/www/uploads/exploit.jpg/test.txt。尽管我们指向的是test.txt但在解析PHAR时EvilClass的__destruct()方法已被触发。核心要点phar://反序列化漏洞的可怕之处在于它不依赖于unserialize()函数而是利用了PHP底层对PHAR格式文件的解析特性。只要能用phar://协议去“触碰”那个文件漏洞就可能被触发。这在文件上传文件包含/读取的场景中极为常见。2.2.5 zip:// 与 compress.zlib://压缩包内的路径穿越这两个协议用于访问压缩包内的文件。其利用方式往往与路径遍历Directory Traversal结合。利用格式zip://archive.zip#dir/file.txtcompress.zlib://archive.gz(对于gzip单文件)注意zip://中的#需要URL编码为%23因为#在URL中是片段标识符。漏洞场景 假设一个应用允许用户上传ZIP文件并会解压到某个目录。如果解压逻辑没有安全处理攻击者可以构造一个包含恶意文件如shell.php的ZIP包并且这个文件的路径是../../../var/www/html/shell.php。那么解压时由于路径遍历这个文件就可能被写到Web目录之外甚至覆盖关键文件。 更进一步如果应用提供了“读取ZIP包内某个文件”的功能并且参数可控攻击者就可以直接利用zip://协议结合路径遍历读取服务器上其他位置的敏感文件例如?filezip:///tmp/upload.zip%23../../../../etc/passwd。3. 典型漏洞场景与实战案例分析理解了协议本身我们来看它们是如何在真实漏洞中串联起来的。伪协议漏洞很少独立存在它通常是其他漏洞的“助推器”或“转换器”。3.1 文件包含漏洞LFI/RFI的升级利用本地文件包含LFI和远程文件包含RFI是经典漏洞。伪协议极大地扩展了它们的危害。基础LFI?page../../../etc/passwd利用php://filter的LFI?pagephp://filter/readconvert.base64-encode/resource../../../etc/passwd(读取任意文件)利用php://input的LFI到RCE?pagephp://input(POST Body:?php system(‘ls’);?)前提是allow_url_includeOn。利用data://的LFI到RCE?pagedata://text/plain;base64,PD9waHAgc3lzdGVtKCdscycpOz8利用phar://的LFI到反序列化RCE?pagephar:///path/to/uploaded/poison.jpg/shell.php(触发反序列化)实战场景模拟 假设一个CMS的模板加载函数如下function loadTemplate($name) { include(‘./templates/’ . $name . ‘.php’); }攻击者发现$name完全可控。他无法直接远程包含但可以先尝试../../../etc/passwd发现被过滤或报错。尝试php://filter/readconvert.base64-encode/resource./templates/index成功读取到index.php的源码从中发现数据库配置文件的路径。再通过php://filter读取数据库配置文件获取数据库凭证。如果服务器开启allow_url_include直接尝试php://input或data://执行命令获取Webshell。3.2 文件上传漏洞的链式攻击单独的文件上传如果只能上传到非Web目录或后缀被严格校验危害有限。但结合伪协议危害可能剧增。场景A上传phar反序列化。如上文所述上传一个伪装成图片的PHAR文件再找到一个触发phar://协议的文件操作点如图片查看功能?imgphar://uploads/xxx.jpg即可RCE。场景B上传zip://路径穿越读取。应用允许上传ZIP并查看其中内容。攻击者上传一个包含恶意路径文件的ZIP通过zip://协议尝试读取系统文件。场景C上传日志污染LFI。应用将上传文件名记录到日志。攻击者上传文件名为?php phpinfo();?.jpg。虽然文件本身不被执行但日志文件如/var/log/app/upload.log里写入了这段代码。随后攻击者利用一个LFI漏洞包含这个日志文件?filephp://filter/readconvert.base64-encode/resource/var/log/app/upload.log或者直接?file/var/log/app/upload.log如果日志文件在Web目录下从而执行代码。3.3 反序列化漏洞的触发入口传统的反序列化漏洞需要找到unserialize()函数。但phar://协议为我们提供了另一种更隐蔽的触发方式。在一些“文件管理器”、“文档查看器”、“缓存读取”等功能中代码可能使用file_get_contents()、fopen()等函数处理用户提供的路径。如果攻击者能控制路径开头将其指向一个已上传的恶意PHAR文件就能在不直接调用unserialize()的情况下触发反序列化。这使得漏洞挖掘的入口点更多元。3.4 SSRF服务端请求伪造中的协议滥用在SSRF漏洞中攻击者诱使服务器向内部或第三方系统发起请求。伪协议可以扩大SSRF的影响读取本地文件如果SSRF点支持file://协议可以直接读取服务器本地文件如file:///etc/passwd。探测内网端口结合http://协议可以扫描内网存活主机和开放端口。与gopher协议结合gopher://是一个更强大的协议可以构造任意格式的TCP数据包常用于攻击内网的Redis、Memcached、MySQL等未授权或弱口令服务。虽然PHP默认不包含gopher封装器但某些扩展或自定义封装器可能支持这是SSRF攻击的“黄金武器”。4. 漏洞挖掘、测试与防御加固指南4.1 黑盒与灰盒测试方法论黑盒测试无源码参数收集收集所有可能接受文件路径、URL、包含模块名的参数如file、page、url、path、include等。协议探测尝试直接读取?file../../../../etc/passwd尝试php://filter?filephp://filter/readconvert.base64-encode/resourceindex.php尝试php://input?filephp://input(POST Body带代码)观察响应延迟或变化。尝试data://?filedata://text/plain,?php echo ‘test’;?尝试phar://需要先有一个文件上传点。上传一个文件后用phar://协议去引用它如?filephar:///tmp/upload/test.jpg。尝试zip://或compress.zlib://?filezip:///tmp/test.zip%23shell.php错误信息分析观察不同协议输入后的错误回显。例如file://协议错误可能显示路径问题php://filter错误可能显示过滤器错误这都能帮助确认协议是否被支持以及过滤情况。自动化工具辅助使用Burp Suite的Intruder模块加载伪协议Payload字典进行Fuzz测试。灰盒/白盒测试有源码或部分信息代码审计关键函数全局搜索include、require、include_once、require_once、file_get_contents、fopen、file、readfile、highlight_file、show_source等函数。跟踪参数传递检查这些函数的参数是否用户可控来自$_GET、$_POST、$_COOKIE、$_REQUEST或者经过简单的拼接后可控。检查过滤逻辑查看是否有对输入进行过滤。常见的错误过滤包括只过滤../但不过滤..\Windows。只过滤http://但不过滤https://、ftp://、php://等。使用str_replace一次性替换但可通过嵌套绕过如..././替换掉../后变成./。黑名单机制总有遗漏。检查服务器配置如果可能尝试获取phpinfo()信息查看allow_url_fopen和allow_url_include的值。4.2 常见WAF与过滤绕过技巧攻击与防御是矛与盾的关系。了解绕过技巧才能更好地防御。编码绕过URL编码php://filter中的/可以编码为%2F:编码为%3A。../可以编码为%2e%2e%2f或..%2f。双重编码某些WAF只解码一次可以对Payload进行两次URL编码。Base64编码data://协议天然支持base64。Rot13编码php://filter的string.rot13过滤器。协议组合与嵌套php://filter/convert.base64-encode/本身是合法的。尝试使用compress.zlib://php://filter/...这种嵌套方式可能绕过简单的字符串匹配。特殊字符与截断在旧版本PHP中空字节截断%00在PHP版本小于5.3.4且magic_quotes_gpcOff时?file../../../../etc/passwd%00可以截断后面的后缀。现在已基本失效。路径长度截断超长路径在某些系统上会被截断但也不常见了。利用Windows特性Windows下路径支持\也支持file://协议的网络路径格式如file://\\127.0.0.1\C$\Windows\win.ini需PHP配置和权限支持。核心绕过思路所有绕过都基于一个前提——防御方的检测或过滤逻辑存在不完整或可被预测的缺陷。因此防御的核心在于使用白名单和严格的正则匹配而非黑名单。4.3 企业级防御方案与安全编码实践防御伪协议攻击需要从配置、代码、架构多个层面入手。1. 服务器配置加固治本之策关闭危险配置在生产环境中务必在php.ini中设置allow_url_fopen Off allow_url_include Off这是最有效、最根本的防御措施。关闭后http://、ftp://、php://input、data://等远程或数据流协议将无法在包含函数和部分文件操作中使用。限制PHP可访问目录设置open_basedir将PHP脚本的文件操作限制在指定的目录树中。及时更新PHP版本老版本PHP存在更多与协议处理相关的历史漏洞。2. 安全编码规范白名单机制对于文件包含、模块加载等功能严格使用白名单。只允许包含预定义好的、安全的文件。$allowed_pages [‘home’, ‘about’, ‘contact’]; $page $_GET[‘page’]; if (in_array($page, $allowed_pages)) { include(‘./templates/’ . $page . ‘.php’); } else { include(‘./templates/404.php’); }避免动态包含如果可能尽量避免使用用户输入直接进行文件包含。使用路由映射或前端控制器模式。严格过滤用户输入如果必须使用动态路径应对输入进行严格校验和过滤。使用basename()函数获取路径中的文件名部分去除目录。使用正则表达式进行严格的白名单匹配例如只允许字母数字和下划线preg_match(‘/^[a-zA-Z0-9_]$/’, $input)。绝对不要使用str_replace来黑名单过滤../应使用realpath()函数解析绝对路径然后检查该路径是否在允许的目录内。$user_input $_GET[‘file’]; $base_dir ‘/var/www/html/uploads/’; $real_path realpath($base_dir . $user_input); // 检查解析后的真实路径是否以允许的基目录开头 if ($real_path false || strpos($real_path, $base_dir) ! 0) { die(‘Invalid file path.’); } // 安全地使用$real_path文件上传安全重命名上传文件使用随机生成的文件名如UUID避免用户控制文件名。将上传文件存储在Web根目录之外通过脚本代理访问。严格检查文件内容MIME类型、文件头而不仅仅依赖后缀名。对于图片使用GD库或ImageMagick进行二次渲染破坏可能嵌入的恶意代码。3. 架构与运维层面最小权限原则运行PHP-FPM或Apache进程的用户权限应尽可能低避免使用root或高权限账户。部署Web应用防火墙WAF在网关层面部署WAF可以拦截常见的伪协议攻击Payload。但WAF是辅助不能替代安全的代码。定期安全审计与渗透测试对代码进行人工审计和自动化扫描定期进行渗透测试主动发现潜在漏洞。5. 高级利用技巧与前沿漏洞研究除了上述常见协议还有一些更隐蔽或版本相关的技巧。5.1 expect:// 协议已基本绝迹expect://是一个用于处理交互式流的协议需要安装PECL的expect扩展。如果启用它可以执行系统命令如expect://ls。由于极度危险现代PHP环境默认不安装此扩展几乎见不到。5.2 利用 php://fd 协议php://fd允许直接访问文件描述符。例如php://fd/0是标准输入。在某些极其特殊的场景下如果攻击者能控制文件描述符编号或许能读取到意外打开的文件。但利用条件极为苛刻实际威胁很小。5.3 过滤器链的巧妙组合php://filter的过滤器可以串联实现复杂的数据处理。例如php://filter/readstring.toupper|string.rot13|convert.base64-encode/resource/etc/passwd这个过滤器链会先将文件内容转为大写然后进行rot13编码最后再进行base64编码。虽然这更多是用于混淆或绕过一些简单的字符串匹配但也展示了过滤器的灵活性。攻击者可能会尝试各种过滤器组合以绕过对特定关键词如?php的检测。5.4 与PHP版本特性结合的漏洞不同PHP版本对协议的处理有细微差别。例如在特定版本下php://filter的write过滤器链可能被滥用进行特定字符的转换从而向文件中写入有效PHP代码。安全研究人员会持续关注PHP内核变更寻找新的攻击面。保持PHP版本更新是防御此类未知漏洞的最好方法。理解PHP伪协议就像是掌握了一套Web安全的“内功心法”。它本身不是漏洞但却是将许多低危漏洞转化为高危漏洞的催化剂。作为开发者知其危险而不用其险作为安全人员知其锋利而善查其迹。在实战中遇到文件操作功能多问一句“用户输入是否可控”多试一次“伪协议是否可用”很多潜在的风险就能被提前发现和规避。安全是一个持续的过程对底层机制的深刻理解是构建有效防御的基石。