如何从非公开存储文件夹读取文件并在前端安全展示?解决readfile失效问题
从非公开存储文件夹安全展示文件的解决方案
核心思路是通过后端代理脚本做中间层:先验证用户授权,再读取非公开文件夹里的文件输出给前端,绝对不能让前端直接访问非公开路径。下面给两种可行的实现方法:
方法一:PHP代理脚本(通用方案)
适合大多数PHP环境,补全权限验证和安全校验后,readfile就能正常工作:
<?php // 第一步:权限验证(按你的业务逻辑调整,比如检查用户登录状态) session_start(); // 示例:仅登录且有权限的用户可访问 if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'authorized') { http_response_code(403); exit('未授权访问'); } // 第二步:严格校验文件名,防止路径遍历攻击 // 维护允许访问的文件白名单,绝对不能直接用用户输入拼接路径! $allowed_files = ['something.jpg', 'report.pdf', 'data.xlsx']; $target_file = $_GET['file'] ?? ''; if (!in_array($target_file, $allowed_files)) { http_response_code(404); exit('文件不存在'); } // 第三步:拼接非公开文件夹的绝对路径(要放在网站根目录之外) $private_folder = '/var/private_storage/'; $file_path = $private_folder . $target_file; // 第四步:检查文件是否存在且可读 if (!file_exists($file_path) || !is_readable($file_path)) { http_response_code(404); exit('文件不存在或无法读取'); } // 第五步:设置响应头,让浏览器识别文件类型 $mime_type = mime_content_type($file_path); header("Content-Type: $mime_type"); header("Content-Length: " . filesize($file_path)); // 输出文件内容 readfile($file_path); exit; ?>
关键注意事项:
- 非公开文件夹必须放在网站根目录之外,比如
/var/private_storage/,不能是/var/www/html/private/,避免用户直接通过URL访问。 - 权限验证必须做:用session、JWT等方式验证用户身份,确保只有授权用户能获取文件。
- 文件名必须用白名单过滤:禁止直接使用
$_GET['file']拼接路径,防止攻击者用../../etc/passwd这类路径遍历攻击。 - 确保PHP进程有读取该文件夹的权限:比如给
/var/private_storage/设置www-data用户可读权限。
方法二:Nginx X-Accel-Redirect(高效方案,适合大文件)
如果服务器用Nginx,这个方法比PHP直接输出更高效——文件传输交给Nginx处理,PHP只负责权限校验:
1. PHP校验脚本(比如serve_private.php)
<?php session_start(); // 先做权限验证 if (!isset($_SESSION['user_id']) || !$_SESSION['has_access']) { http_response_code(403); exit('未授权访问'); } $allowed_files = ['something.jpg']; $target_file = $_GET['file'] ?? ''; if (!in_array($target_file, $allowed_files)) { http_response_code(404); exit('文件不存在'); } // 告诉Nginx内部重定向到私有文件路径 header("X-Accel-Redirect: /internal/private/" . $target_file); // 可选:禁止缓存,或按需设置缓存策略 header("Cache-Control: no-cache"); exit; ?>
2. Nginx配置修改
在站点配置中添加以下内容:
location /internal/private/ { internal; # 仅允许内部请求,禁止用户直接访问 root /var/private_storage/; # 指向你的非公开文件夹 }
前端调用方式
不管用哪种方法,前端都通过代理脚本访问文件:
<!-- 展示图片 --> <img src="/serve_private_file.php?file=something.jpg" alt="私有图片"> <!-- 下载文件 --> <a href="/serve_private_file.php?file=report.pdf" download>下载私有报告</a>
内容的提问来源于stack exchange,提问作者Hello123G
相关产品推荐
相关产品推荐

