加固php2shell漏洞后的文件下载脚本,核心在于彻底禁用`shell_exec`等危险函数调用,并实施严格的白名单校验机制。具体做法是移除所有系统命令执行代码,改用php原生函数如`readfile()`或`fopen()`配合`basename()`进行安全读取。同时,必须对输入的文件路径进行严格过滤,确保其位于指定的允许目录内,防止目录穿越攻击。这样处理后,即使攻击者尝试注入恶意参数,也无法触发系统级命令,从而保障服务器安全。

如何验证旧版脚本中的危险函数调用
旧版脚本通常直接使用`shell_exec`或反引号执行系统命令来下载文件,这是导致PHP2Shell漏洞的根本原因。通过静态分析或代码审查,可以定位到这些高危调用点。例如,检查代码中是否包含`exec()`, `system()`, `passthru()`, `proc_open()`以及反引号操作符。一旦确认存在此类调用,应立即标记为高风险区域,因为攻击者可以通过构造特殊的文件名参数(如包含`; rm -rf /`)来执行任意系统指令。

$filename = $_GET['file'];
// 危险:直接执行系统命令
shell_exec('wget http://example.com/' . $filename);
// 危险:使用反引号
$output = `cat /var/www/html/$filename`;上述代码展示了典型的危险用法。当`$filename`被赋值为`test.txt; cat /etc/passwd`时,系统将执行两个命令,导致敏感信息泄露。因此,第一步加固措施是彻底删除所有涉及系统命令执行的代码行,替换为纯PHP逻辑处理。
立即学习“PHP免费学习笔记(深入)”;
如何实施安全的文件读取与路径校验
在移除危险函数后,应使用PHP内置的文件处理函数替代。推荐使用`readfile()`直接输出文件内容,或使用`fopen()`结合`fgets()`逐行读取以支持流式传输。关键在于路径校验:必须使用`realpath()`获取文件的绝对路径,并检查该路径是否以允许的基目录开头。此外,使用`basename()`剥离路径中的目录部分,防止`../`等目录穿越序列。只有当校验通过且文件确实存在时,才允许读取和输出。

$allowed_dir = '/var/www/allowed_files/';
$file = basename($_GET['file']);
$full_path = realpath($allowed_dir . $file);
if ($full_path === false || strpos($full_path, $allowed_dir) !== 0) {
http_response_code(403);
die('Access denied');
}
if (file_exists($full_path)) {
readfile($full_path);
} else {
http_response_code(404);
die('File not found');
}这段代码首先通过`basename()`清理输入,然后构建完整路径并使用`realpath()`解析符号链接和相对路径。接着,通过`strpos()`检查解析后的路径是否仍在允许的目录下。如果校验失败,直接拒绝访问并返回403错误。这种机制能有效阻止目录穿越攻击,确保只能访问预设目录内的文件。对于大文件,可结合`header()`设置`Content-Type`和`Content-Disposition`,实现标准的文件下载行为。
基于特定输入条件下的权限控制结果
当输入包含特殊字符或试图访问上级目录时,加固后的脚本会直接拦截请求。例如,若用户传入`file=../../etc/passwd`,`basename()`会将其截断为`passwd`,但`realpath()`解析后的路径将指向`/var/www/allowed_files/passwd`。由于该路径不以`/var/www/allowed_files/`开头(假设`/etc/passwd`不在该目录下),校验将失败,脚本返回403 Forbidden。这种基于实际路径的校验比简单的字符串匹配更安全,因为它能正确处理符号链接和复杂的路径结构。开发者应始终依赖`realpath()`的结果进行最终决策,而非仅信任用户输入或初步拼接的路径。



















