通过设置http响应头并配合白名单校验,可以强制浏览器下载文件并防止类型混淆。具体做法是读取文件内容后,在输出前检查文件名后缀是否在预定义的允许列表中,若匹配则发送`content-disposition: attachment`头,否则拒绝访问。

header()如何触发下载行为
使用header()函数设置Content-Type为application/octet-stream,并添加Content-Disposition头指定文件名,即可让浏览器弹出保存对话框而非直接预览。关键在于必须确保在发送任何HTML或文本之前调用这些函数,且需关闭输出缓冲以避免乱码。

function forceDownload($filePath, $fileName) {
if (!file_exists($filePath)) {
http_response_code(404);
exit('File not found');
}
// 设置下载头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($fileName) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filePath));
// 清除输出缓冲
ob_clean();
flush();
// 输出文件内容
readfile($filePath);
exit;
}
预期结果:浏览器接收到请求后,不会尝试渲染内容,而是根据filename参数提示用户保存文件,无论文件实际扩展名是什么。
白名单校验如何实现安全过滤
在提供下载服务前,提取文件扩展名并与允许的列表进行比对。只有当扩展名存在于白名单中时才执行下载逻辑,否则返回错误信息。这种方法能有效防止用户上传恶意脚本并通过修改后缀名执行。

$allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'png', 'jpg'];
$fileName = $_GET['file'] ?? '';
$path = './uploads/' . $fileName;
// 获取扩展名
$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (in_array($ext, $allowedExtensions)) {
forceDownload($path, $fileName);
} else {
echo 'Invalid file type';
}
预期结果:对于test.pdf,程序进入下载流程;对于malware.php,程序输出“Invalid file type”并终止,不执行任何下载操作。
基于输入$_GET['file']的目录穿越防护
当直接使用$_GET['file']作为路径的一部分时,攻击者可能传入../../etc/passwd等路径。必须在构建完整路径前对文件名进行规范化处理,仅保留基础文件名部分,从而消除目录穿越风险。
在forceDownload函数内部,应使用basename()函数剥离路径中的目录信息,确保最终访问的文件始终位于预期的上传目录下。结合白名单校验,双重保障安全性。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











