You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WordPress特定文件目录权限控制:仅管理员与所有者可访问

WordPress 用户私有文件目录权限控制解决方案

需求说明

  • 隐藏目录wp-content/uploads/useruploads/[user_nicename][user_id]/下的所有文件,禁止直接通过URL访问
  • 仅管理员和该文件夹的所有者用户可查看、下载目录内的文件
  • 用户可上传文件,上传时自动生成与用户匹配的专属文件夹

现有尝试与问题

  1. .htaccess 访问限制:已添加规则禁止直接访问该目录,这一步是正确的,避免了文件被直接请求
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-content/uploads/useruploads/ - [F,L]
</IfModule>
  1. 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>

安全建议

  1. 文件权限设置:将useruploads目录及其子目录权限设为750,文件权限设为640,禁止其他用户访问
  2. 整合到WordPress规范:建议将下载处理逻辑做成自定义端点(通过add_rewrite_endpoint),而非单独PHP文件
  3. 输入过滤:始终对用户传入的file、dir等参数过滤验证,防止路径遍历攻击
  4. 日志记录:添加访问日志,记录管理员和用户的文件访问行为,便于排查异常

内容的提问来源于stack exchange,提问作者tnebrekooy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 07:20:27