必须加鉴权,否则是生产事故;PHP 8.3.36 无文件删除权限控制,漏洞源于业务代码未校验;修复核心是删除前验证用户对目标文件的权限,禁用前端传参直接操作。

直接结论:这个接口必须加鉴权,不加就是生产事故。 PHP 8.3.36 本身不带文件删除权限控制逻辑,所谓“漏洞”是业务代码没做校验,不是 PHP 版本问题。修复核心就一条:所有删除操作前,必须验证当前请求是否具备对目标文件的删除权限,且不能只靠前端传来的 file_id 或路径字符串做判断。
为什么删文件接口容易漏鉴权
常见错误是把“能访问到文件路径”当成“有权删它”。比如接口接收一个 path 参数,后端直接 unlink($_GET['path']),完全没检查:当前用户是否拥有该文件、该文件是否属于当前租户、路径是否被篡改(如 ../../etc/passwd)、是否在允许的操作目录内。
- 攻击者可构造恶意
path绕过目录限制,删除任意系统文件 - 多租户场景下,若用
file_id=123查库删记录,但没校验该file_id是否归属当前用户,就会越权删除他人文件 - 即使用了相对路径拼接,如
__DIR__ . '/uploads/' . $_POST['filename'],一旦filename含../且未过滤,仍可穿透
PHP 8.3 中安全删除的三步校验
不要跳步。每一步都得有,缺一不可:
-
路径规范化 + 白名单校验:用
realpath()解析完整路径,再用str_starts_with()强制限定在指定根目录下,例如str_starts_with($realPath, $_SERVER['DOCUMENT_ROOT'] . '/uploads/') -
数据库权限映射:如果文件由业务表关联(如
user_files表),必须通过file_id查出该记录,并比对user_id字段是否等于当前登录用户的 ID —— 不能只查存在性 -
操作日志与二次确认(可选但推荐):对高危删除(如配置文件、用户头像),记录
user_id、file_path、ip、timestamp到审计表;敏感操作建议走异步任务或加软删除标记,而非立即unlink()
典型错误代码 vs 修复后对比
错误写法(危险!):
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
立即学习“PHP免费学习笔记(深入)”;
// ❌ 危险:无路径校验、无权限比对、无类型检查
if (isset($_GET['path'])) {
unlink($_GET['path']);
}
修复后写法(最小安全闭环):
// ✅ 安全:路径规范 + 目录白名单 + 数据库归属校验
$fileId = (int)$_POST['file_id'] ?? 0;
if ($fileId <= 0) {
http_response_code(400);
exit('Invalid file ID');
}
$stmt = $pdo->prepare('SELECT path, user_id FROM user_files WHERE id = ? AND status = "active"');
$stmt->execute([$fileId]);
$file = $stmt->fetch();
if (!$file || $file['user_id'] !== $_SESSION['user_id']) {
http_response_code(403);
exit('Forbidden');
}
$realPath = realpath($file['path']);
$allowedRoot = $_SERVER['DOCUMENT_ROOT'] . '/uploads/';
if (!$realPath || !str_starts_with($realPath, $allowedRoot)) {
http_response_code(400);
exit('Invalid file path');
}
if (unlink($realPath)) {
$pdo->prepare('UPDATE user_files SET status = "deleted" WHERE id = ?')->execute([$fileId]);
}
最后提醒一句:别信任何“前端隐藏了删除按钮所以后端不用鉴权”的说法。只要接口 URL 和参数能被构造出来,就没所谓“隐藏”。真正的防线永远在服务端最后一次 unlink() 调用之前。


















