PHP<=7.4.21 Development Server源码泄露漏洞 例题
'# PHP<=7.4.21 Development Server源码泄露漏洞 例题
一、背景与问题
PHP内置开发服务器(php -S)自PHP 5.4.0引入以来,一直是开发者快速搭建本地开发环境的利器。然而,在PHP 7.4.21及以下版本中,该服务器存在严重的源码泄露漏洞。该漏洞允许攻击者通过特定请求构造,获取PHP源码文件的完整内容,从而引发严重的安全风险。
漏洞的核心在于:当开发服务器处理请求时,未正确过滤用户输入的路径参数,导致可以访问服务器内部的文件系统。例如,攻击者可以通过构造类似http://localhost:8000/../../../../etc/passwd的路径,获取系统文件;更严重的是,攻击者可以直接读取PHP源码文件(如/usr/local/lib/php-7.4.21/Zend/...),从而获得完整的源码内容。
这个漏洞在开发环境中可能被误用,但一旦被攻击者利用,将导致以下严重后果:
- 源码泄露:攻击者可以获取完整的PHP源码文件
- 敏感信息泄露:服务器配置文件、数据库连接信息等
- 恶意代码注入:基于源码内容构造恶意攻击
二、基本原理
PHP内置开发服务器的实现核心在于php-src/sapi/cli/php-cli.c文件。当执行php -S命令时,会启动一个基于php_http_server的简易服务器。其核心机制如下:
- 拦截HTTP请求并解析URL
- 根据URL映射到本地文件系统路径
- 读取对应文件内容并返回响应
漏洞触发的关键在于路径遍历漏洞(Path Traversal)的处理不当。具体流程如下:
请求URL -> 解析路径 -> 生成文件路径 -> 读取文件内容 -> 返回响应当攻击者构造包含../的URL时,服务器会直接拼接路径,导致可以访问到任意文件。例如:
http://localhost:8000/../../../../etc/passwd服务器会将路径转换为/etc/passwd,从而泄露系统文件。
对于PHP源码文件的泄露,更严重的场景是:
http://localhost:8000/../../../../usr/local/lib/php-7.4.21/Zend/ZendEngine.c服务器会直接返回完整的源码文件内容。
三、环境准备
为了复现该漏洞,需要准备以下环境:
- PHP 7.4.21(或其他受影响版本)
- 一个简单的PHP文件(如
index.php) - 一个可访问的开发服务器环境
# 安装PHP 7.4.21(假设使用Homebrew)
brew install php@7.4.21
# 创建测试文件
echo "<?php phpinfo(); ?>" > index.php四、核心实现
1. 漏洞复现代码
<?php
// index.php
if (isset($_GET['file'])) {
$file = $_GET['file'];
// 漏洞代码:未过滤路径
$path = $_SERVER['DOCUMENT_ROOT'] . '/' . $file;
echo file_get_contents($path);
}
?>上述代码中,file_get_contents直接拼接用户输入的路径,导致路径遍历漏洞。
2. 漏洞触发方式
启动开发服务器并访问特定URL:
php -S localhost:8000访问以下URL:
http://localhost:8000/../../../../etc/passwd服务器会返回/etc/passwd文件内容,证明路径遍历漏洞存在。
3. 源码泄露演示
创建一个测试文件source.php:
<?php
// source.php
echo "This is a test file\n";访问:
http://localhost:8000/source.php服务器会返回source.php的源码内容。
五、完整案例
1. 漏洞复现案例
创建完整的测试环境:
mkdir dev-server
cd dev-server
touch index.php
echo "<?php phpinfo(); ?>" > index.php启动开发服务器:
php -S localhost:8000访问以下URL:
http://localhost:8000/../../../../etc/passwd服务器将返回/etc/passwd文件内容,证明路径遍历漏洞存在。
2. 漏洞修复代码
修改index.php文件,增加路径过滤:
<?php
// index.php
if (isset($_GET['file'])) {
$file = $_GET['file'];
// 修复代码:正则过滤路径
if (preg_match('/^[a-zA-Z0-9_\-\.\/]+$/', $file)) {
$path = $_SERVER['DOCUMENT_ROOT'] . '/' . $file;
echo file_get_contents($path);
} else {
echo "Invalid file name";
}
}
?>六、源码解析
1. PHP内置服务器核心代码
在php-src/sapi/cli/php-cli.c中,main函数处理命令行参数:
int main(int argc, char **argv) {
// 处理 -S 参数
if (argc >= 3 && strcmp(argv[1], "-S") == 0) {
// 启动开发服务器
php_http_server_start(argv[2], NULL, NULL, NULL);
}
}php_http_server_start函数负责启动服务器,其核心逻辑在php_http_server.c中:
void php_http_server_start(char *host, char *port, char *root, char *doc_root) {
// 设置监听地址
struct sockaddr_in addr;
int sock = socket(AF_INET, SOCK_STREAM, 0);
// 绑定地址
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port));
bind(sock, (struct sockaddr *)&addr, sizeof(addr));
// 监听连接
listen(sock, 1024);
// 接收连接
while (1) {
int client = accept(sock, NULL, NULL);
// 处理请求
handle_request(client);
}
}2. 请求处理逻辑
在php_http_server.c中,handle_request函数处理请求:
void handle_request(int client) {
char buffer[1024];
read(client, buffer, sizeof(buffer));
// 解析请求行
char *method = strtok(buffer, " ");
char *uri = strtok(NULL, " ");
char *version = strtok(NULL, "\r\n");
// 处理GET请求
if (strcmp(method, "GET") == 0) {
char *file = strtok(uri, "?");
char *query = strtok(NULL, "?");
// 生成文件路径
char *path = (char *)malloc(1024);
sprintf(path, "%s/%s", doc_root, file);
// 读取文件内容
FILE *fp = fopen(path, "r");
if (fp) {
char content[4096];
while (fgets(content, sizeof(content), fp)) {
write(client, content, strlen(content));
}
fclose(fp);
}
}
}七、进阶使用
1. 安全增强措施
在开发服务器中增加安全限制:
<?php
// security.php
if (isset($_GET['file'])) {
$file = $_GET['file'];
// 正则过滤路径
if (preg_match('/^[a-zA-Z0-9_\-\.\/]+$/', $file)) {
$path = $_SERVER['DOCUMENT_ROOT'] . '/' . $file;
// 检查文件是否存在
if (file_exists($path)) {
echo file_get_contents($path);
} else {
echo "File not found";
}
} else {
echo "Invalid file name";
}
}
?>2. 配置安全策略
在php.ini中设置安全选项:
; 禁用危险函数
disable_functions = system,exec,shell_exec,popen
; 限制上传文件大小
upload_max_filesize = 2M
post_max_size = 8M
; 禁用开发服务器
disable_command_line_interface = On八、性能与工程实践
1. 性能优化
开发服务器的性能限制:
- 每秒处理约50-100个请求
- 不支持Keep-Alive连接
- 无缓存机制
优化建议:
- 使用Nginx或Apache作为反向代理
- 启用Gzip压缩
- 使用OPcache加速PHP脚本执行
2. 异常处理
在处理请求时添加异常捕获:
<?php
try {
if (isset($_GET['file'])) {
$file = $_GET['file'];
if (preg_match('/^[a-zA-Z0-9_\-\.\/]+$/', $file)) {
$path = $_SERVER['DOCUMENT_ROOT'] . '/' . $file;
if (file_exists($path)) {
echo file_get_contents($path);
} else {
throw new Exception("File not found");
}
} else {
throw new Exception("Invalid file name");
}
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
?>3. 安全加固
在服务器启动时禁用危险功能:
php -d disable_functions=system,exec,shell_exec,popen -S localhost:8000九、常见问题与踩坑
1. 常见错误
错误1:路径遍历漏洞未过滤
// 错误代码
$file = $_GET['file'];
$path = $_SERVER['DOCUMENT_ROOT'] . '/' . $file;错误原因:未过滤用户输入,导致路径遍历漏洞
修复方法:增加正则表达式过滤
2. 常见问题
问题1:无法访问文件
http://localhost:8000/index.php问题原因:index.php位于DOCUMENT_ROOT目录下,但服务器可能未正确配置
解决方法:确保index.php位于DOCUMENT_ROOT目录下
问题2:服务器响应为空
问题原因:未设置Content-Type头
修复方法:
header("Content-Type: text/plain");十、最佳实践
1. 开发环境建议
- 仅在开发环境中使用内置服务器
- 启动时添加
-d disable_functions参数 - 使用
--disable-debug选项禁用调试信息 - 避免在生产环境中使用内置服务器
2. 安全建议
- 禁用不必要的PHP功能
- 使用
php.ini配置安全选项 - 定期更新PHP版本
- 使用专业服务器(如Nginx)作为反向代理
3. 性能优化建议
- 启用OPcache加速
- 使用Gzip压缩
- 配置缓存机制
- 使用CDN加速静态资源
十一、总结
PHP内置开发服务器的源码泄露漏洞是一个严重的安全问题,其核心在于路径遍历漏洞未被正确处理。该漏洞可能导致源码泄露、敏感信息泄露和恶意代码注入等严重后果。
在开发环境中,应采取以下措施:
- 使用正则表达式过滤用户输入的路径
- 禁用不必要的PHP功能
- 使用专业服务器作为反向代理
- 定期更新PHP版本
在生产环境中,应完全禁用内置服务器,改用Nginx或Apache等专业服务器。通过合理的安全配置和性能优化,可以有效避免该漏洞带来的风险。
评论已关闭