如何禁止通过直接访问URL下载Web服务器上传目录内的文件
解决方案整体思路
核心是通过PHP做下载权限校验层,同时屏蔽uploads目录的直接外部访问,所有下载请求都走PHP校验逻辑后再返回文件内容。
第一步:禁止直接访问uploads目录的文件
因为你无法将文件放到htdocs外、也无法修改目录权限,直接通过Web服务器配置屏蔽目录外部访问即可:
- 如果你用的是Apache服务器,在
htdocs/uploads目录下新建.htaccess文件,写入以下内容:
Deny from all
- 如果你用的是Nginx服务器,在站点的Nginx配置文件中添加如下规则:
location ~* ^/uploads/ { deny all; return 403; }
配置完成后,即使用户知道完整的文件URL,也会直接返回403拒绝访问。
第二步:开发PHP下载代理脚本
在站点根目录新建download.php文件,负责登录校验、权限校验、文件输出,参考代码如下:
<?php session_start(); // 1. 先校验用户登录状态 if (!isset($_SESSION['user']['id'])) { http_response_code(403); die('无访问权限,请先登录'); } $current_user_id = $_SESSION['user']['id']; // 2. 校验传入的文件ID参数 if (empty($_GET['file_id']) || !is_numeric($_GET['file_id'])) { http_response_code(400); die('参数错误'); } $file_id = intval($_GET['file_id']); // 3. 从数据库查询文件信息,同时校验文件归属 // 此处替换为你自己的数据库连接和查询逻辑 $pdo = new PDO('mysql:host=你的数据库地址;dbname=你的库名', '数据库账号', '数据库密码'); $stmt = $pdo->prepare("SELECT filename, storage_path FROM 你的文件表名 WHERE id = ? AND user_id = ?"); $stmt->execute([$file_id, $current_user_id]); $file_info = $stmt->fetch(PDO::FETCH_ASSOC); if (!$file_info) { http_response_code(404); die('文件不存在或你无权限访问该文件'); } // 4. 拼接文件物理路径,校验文件是否真实存在 $physical_path = $_SERVER['DOCUMENT_ROOT'] . str_replace('你的站点域名/', '/', $file_info['storage_path']); if (!file_exists($physical_path) || is_dir($physical_path)) { http_response_code(404); die('文件不存在'); } // 5. 输出下载头,返回文件内容 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $file_info['filename'] . '"'); header('Content-Length: ' . filesize($physical_path)); // 清空缓冲区避免额外输出导致文件损坏 ob_clean(); flush(); readfile($physical_path); exit; ?>
第三步:修改页面内的下载链接
把原来直接指向文件URL的下载链接,替换为指向代理脚本+文件ID的格式:
原代码:
<a href="' . $row['path'] . '" target="_blank" download>Download file</a>
修改后:
<a href="/download.php?file_id=' . $row['id'] . '" target="_blank" download>Download file</a>
其中$row['id']是你文件数据表对应的主键ID。
可选安全优化
- 禁止在下载脚本中直接传递文件路径作为参数,避免路径遍历漏洞,所有路径必须通过文件ID从数据库查询获取
- 可以额外增加CSRF Token校验,防止恶意链接诱导用户下载
- 如果需要支持大文件下载,可以把
readfile替换成分段读取输出的逻辑,避免占用过多内存
内容的提问来源于stack exchange,提问作者Mischa Mustermann
相关产品推荐
相关产品推荐

