WordPress特定文件目录权限控制:仅管理员与所有者可访问
WordPress 用户私有文件目录权限控制解决方案
需求说明
- 隐藏目录
wp-content/uploads/useruploads/[user_nicename][user_id]/下的所有文件,禁止直接通过URL访问 - 仅管理员和该文件夹的所有者用户可查看、下载目录内的文件
- 用户可上传文件,上传时自动生成与用户匹配的专属文件夹
现有尝试与问题
- .htaccess 访问限制:已添加规则禁止直接访问该目录,这一步是正确的,避免了文件被直接请求
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^wp-content/uploads/useruploads/ - [F,L] </IfModule>
- PHP 权限验证脚本:实现了用户匹配后输出文件,但存在两个问题:
- 刷新页面会自动触发文件下载
- 脚本存在逻辑漏洞,需确认安全性
现有脚本问题分析
逻辑错误
原下载脚本中if (!is_user_logged_in())判断写反,导致未登录用户才能进入处理逻辑,完全不符合需求,应改为if (is_user_logged_in())。
自动下载根源
脚本强制设置Content-Disposition: attachment,会让浏览器触发下载。若需在线查看PDF、图片等文件,需根据文件类型动态设置响应头。
安全隐患
- 缺少管理员权限判断,原脚本仅允许前端非管理员访问,不符合“管理员可查看所有文件”的需求
- 存在路径遍历风险:虽用了
sanitize_file_name,但需确保用户无法通过构造参数访问其他目录文件
优化后的完整解决方案
1. 增强版 .htaccess 规则
保留原有规则,添加目录索引禁止:
# 禁止目录索引 Options -Indexes # 禁止直接访问useruploads目录下的文件 <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^wp-content/uploads/useruploads/ - [F,L] </IfModule>
2. 优化后的文件列表展示脚本
添加管理员权限判断,支持管理员查看所有用户目录:
<?php require_once(__DIR__ . '/../../../../wp-load.php'); // 未登录用户直接跳转登录页 if (!is_user_logged_in()) { wp_redirect(wp_login_url()); exit; } $current_user_id = get_current_user_id(); $current_user_info = get_userdata($current_user_id); $username = $current_user_info->user_nicename; // 管理员可查看所有用户目录,普通用户仅查看自己的 if (current_user_can('manage_options')) { $base_dir = WP_CONTENT_DIR . '/uploads/useruploads/'; $directories = glob($base_dir . '*', GLOB_ONLYDIR); echo '<h2>所有用户上传文件目录</h2>'; echo '<ul>'; foreach ($directories as $dir) { $dir_name = basename($dir); echo '<li><a href="#" onclick="loadFiles(\'' . urlencode($dir_name) . '\')">' . $dir_name . '</a></li>'; } echo '</ul>'; } else { $directory_path = WP_CONTENT_DIR . '/uploads/useruploads/' . $username . $current_user_id; if (file_exists($directory_path) && is_dir($directory_path)) { $files = scandir($directory_path); echo '<h2>你的上传文件:</h2>'; echo '<ul>'; foreach ($files as $file) { if ($file != '.' && $file != '..') { $file_url = home_url('/file-access.php?file=' . urlencode($file)); // 支持在线预览的文件用新窗口打开,其他触发下载 $file_ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); $preview_ext = ['pdf', 'jpg', 'jpeg', 'png', 'gif']; if (in_array($file_ext, $preview_ext)) { echo '<li><a href="' . $file_url . '" target="_blank">' . $file . '</a></li>'; } else { echo '<li><a href="#" onclick="downloadFile(\'' . urlencode($file) . '\')">' . $file . '</a></li>'; } } } echo '</ul>'; } else { echo '暂无上传文件或目录不存在'; } } ?> <script> function downloadFile(fileName) { var link = document.createElement('a'); link.href = '<?php echo home_url('/file-access.php?file='); ?>' + fileName; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); } // 管理员加载指定用户目录的文件 function loadFiles(dirName) { fetch('<?php echo home_url('/load-user-files.php?dir='); ?>' + dirName) .then(response => response.text()) .then(html => { document.querySelector('ul').innerHTML = html; }); } </script>
3. 修复并增强的文件下载/预览处理脚本
修正逻辑错误,添加管理员权限支持,动态设置响应头:
<?php require_once(__DIR__ . '/../../../../wp-load.php'); // 未登录用户直接拒绝 if (!is_user_logged_in()) { wp_die('请先登录'); } $current_user_id = get_current_user_id(); $current_user_info = get_userdata($current_user_id); $username = $current_user_info->user_nicename; $file = isset($_GET['file']) ? sanitize_file_name($_GET['file']) : ''; $target_dir = ''; // 管理员可指定要访问的用户目录,普通用户仅能访问自己的 if (current_user_can('manage_options')) { $target_dir = isset($_GET['dir']) ? sanitize_file_name($_GET['dir']) : ''; $directory_path = WP_CONTENT_DIR . '/uploads/useruploads/' . $target_dir; } else { $directory_path = WP_CONTENT_DIR . '/uploads/useruploads/' . $username . $current_user_id; } $file_path = $directory_path . '/' . $file; // 验证目录和文件存在,且防止路径遍历 if (!file_exists($directory_path) || !is_dir($directory_path)) { wp_die('目录不存在或无访问权限'); } if (!file_exists($file_path) || !is_readable($file_path) || strpos(realpath($file_path), realpath($directory_path)) !== 0) { wp_die('文件不存在或无访问权限'); } // 根据文件类型设置响应头 $file_ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); $mime_types = [ 'pdf' => 'application/pdf', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif', 'txt' => 'text/plain', 'doc' => 'application/msword', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ]; $content_type = isset($mime_types[$file_ext]) ? $mime_types[$file_ext] : 'application/octet-stream'; header('Content-Type: ' . $content_type); // 支持在线预览的文件设置为inline,其他设置为attachment $preview_ext = ['pdf', 'jpg', 'jpeg', 'png', 'gif', 'txt']; if (in_array($file_ext, $preview_ext)) { header('Content-Disposition: inline; filename="' . basename($file_path) . '"'); } else { header('Content-Disposition: attachment; filename="' . basename($file_path) . '"'); } header('Content-Length: ' . filesize($file_path)); header('Cache-Control: private'); readfile($file_path); exit; ?>
4. 管理员加载用户文件的辅助脚本(load-user-files.php)
<?php require_once(__DIR__ . '/../../../../wp-load.php'); if (!current_user_can('manage_options')) { wp_die('无访问权限'); } $dir_name = isset($_GET['dir']) ? sanitize_file_name($_GET['dir']) : ''; $directory_path = WP_CONTENT_DIR . '/uploads/useruploads/' . $dir_name; if (!file_exists($directory_path) || !is_dir($directory_path)) { echo '<li>目录不存在</li>'; exit; } $files = scandir($directory_path); $html = ''; foreach ($files as $file) { if ($file != '.' && $file != '..') { $file_url = home_url('/file-access.php?file=' . urlencode($file) . '&dir=' . urlencode($dir_name)); $file_ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); $preview_ext = ['pdf', 'jpg', 'jpeg', 'png', 'gif']; if (in_array($file_ext, $preview_ext)) { $html .= '<li><a href="' . $file_url . '" target="_blank">' . $file . '</a></li>'; } else { $html .= '<li><a href="#" onclick="downloadFile(\'' . urlencode($file) . '\', \'' . urlencode($dir_name) . '\')">' . $file . '</a></li>'; } } } echo $html; ?> <script> function downloadFile(fileName, dirName) { var link = document.createElement('a'); link.href = '<?php echo home_url('/file-access.php?file='); ?>' + fileName + '&dir=' + dirName; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); } </script>
安全建议
- 文件权限设置:将
useruploads目录及其子目录权限设为750,文件权限设为640,禁止其他用户访问 - 整合到WordPress规范:建议将下载处理逻辑做成自定义端点(通过
add_rewrite_endpoint),而非单独PHP文件 - 输入过滤:始终对用户传入的
file、dir等参数过滤验证,防止路径遍历攻击 - 日志记录:添加访问日志,记录管理员和用户的文件访问行为,便于排查异常
内容的提问来源于stack exchange,提问作者tnebrekooy
相关产品推荐
相关产品推荐

