phpinfo漏洞
一个没做任何访问控制的 phpinfo() 页面,就是给攻击者递上了一张写满服务器底牌的名片。很多人觉得这玩意儿不就是看看 PHP 配置吗,能有什么危害。去年某中型电商的一次入侵事件里,攻击者最初拿到的入口,就是一个遗留在 /test/phpinfo.php 的探针文件。从那张页面上,他直接读到了 DOCUMENT_ROOT 的绝对路径、disable_functions 里没禁 system、MySQL 的默认连接主机,甚至 $_SERVER['HTTP_HOST'] 暴露出的内部测试域名。三个小时后,他通过一个任意文件读取漏洞配合这些信息,拿到了数据库的只读账号。
phpinfo 泄露的东西比你印象中要多。除了 PHP 版本和已加载扩展,它还会打印 allow_url_fopen、open_basedir 是否为空、session 的保存路径、上传临时目录、环境变量里的 PATH,以及所有已注册的 HTTP_* 头。如果服务器配置不当,phpinfo() 的输出里甚至可能包含数据库密码——比如某些老版本的 php.ini 中 mysql.default_password 被硬编码,或者环境变量里塞了 MYSQL_PWD。我见过一个案例,运维图省事把 Redis 密码写在了 $_SERVER 的自定义变量里,而 phpinfo 页面恰好把整个 _SERVER 数组 dump 了出来。
为什么这个漏洞至今还频繁出现?因为太多开发者在调试阶段随手扔一个 phpinfo.php 到 web 目录,上线时忘了删。有些 CMS 或框架的安装向导会默认生成 phpinfo 文件,安装完也不清理。更隐蔽的是,某些第三方库或 composer 包在测试目录里带了 phpinfo 示例文件,部署时整个 vendor 目录被暴露在公网。扫描器对 phpinfo.php、info.php、test.php 的探测从来没停过,Shodan 上随便一搜就是几十万个结果。
利用方式很直接。攻击者拿到 PHP 版本后,会去查该版本已知的 CVE。看到 disable_functions 为空,就知道可以尝试命令执行。发现 open_basedir 没设,意味着可以跨目录读文件。如果 allow_url_include 是 On,那远程文件包含的大门就敞着。最不济,phpinfo 页面本身也能被用来做反射型 XSS——某些 phpinfo 输出会回显 $_GET 参数,没做转义的话,构造一个带脚本的 URL 就能打。还有更阴的用法:用 phpinfo 页面做时间盲注的探测点,因为它的响应时间稳定且可预测。
修复这件事没有中间路线。生产环境里绝对不能存在任何可访问的 phpinfo 页面。如果你需要监控 PHP 配置,用 CLI 执行 php -i 输出到文件,或者通过 php-fpm 的 status 页面配合访问控制。临时调试完,立刻删除文件,别只是改个名——phpinfo.php.bak 在某些配置下照样能被解析。Web 服务器层面可以加一条规则,直接拒绝所有包含 phpinfo 的 URI 请求,Nginx 里写 location ~* phpinfo { deny all; } 能挡住大部分低级扫描。但根本还是靠部署流程:把“检查是否有 phpinfo 残留”写进上线 checklist,比事后补救有效得多。
我自己的习惯是,本地开发用 php -S 起内置服务器时随便放 phpinfo,但一旦代码进了 git 仓库,.gitignore 里必须排除所有 *info*.php 模式。CI 流水线里加一步 grep,发现 phpinfo\s*\( 就直接 fail。这比依赖人眼靠谱。毕竟,你永远不知道哪个实习生会在周五下午把调试文件传到生产环境。
(责任编辑:热点)
