问题描述

我有一个非常具体的用例,该网站为律师建立,每个客户都可以登录自己的’specific page/portal'(自定义帖子类型),无需访问 wp-admin 等 (我创建了所有登录/注册/profile-editing 页面前端) 。在这个页面/门户中,律师将留下客户端下载的消息和文件,从理论上说,一个客户端可以猜测 (或者如果有其他客户端文件的知识) 其他文件名称和下载,从而创建隐私/安全性问题/机密材料等

我正在寻找解决方案的想法/概念,我最初的想法是让下载链接指向一些 download.php 发送附件 id,用户 ID,页面/门户网站 ID 和随机数,并在另一端处理。 。

你怎么看?我在正确的轨道上还是这种方法有缺陷?

谢谢!

最佳解决方案

需要做的是,您需要通过 WordPress 为所需的文件类型代理下载请求。假设您要限制对”.doc” 文件的访问。

1. 定义一个查询变量,指示所请求的文件

function add_get_file_query_var( $vars ) {
    $vars[] = 'get_file';
    return $vars;
}
add_filter( 'query_vars', 'add_get_file_query_var' );

2. 更新.htaccess 将受限文件的请求转发到 WordPress

这将捕获您要限制的文件的请求,并使用上面的自定义查询变量将其发送回 WordPress 。在 RewriteCond 线之前插入以下规则。

RewriteRule ^wp-content/uploads/(.*.docx)$ /index.php?get_file=$1

3. 在自定义查询变量中捕获请求的文件名; 并验证对该文件的访问权限:

function intercept_file_request( $wp ) {
    if( !isset( $wp->query_vars['get_file'] ) )
        return;

    global $wpdb, $current_user;

    // Find attachment entry for this file in the database:
    $query = $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE guid='%s'", $_SERVER['REQUEST_URI'] );
    $attachment_id = $wpdb->get_var( $query );

    // No attachment found. 404 error.
    if( !$attachment_id ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Get post from database
    $file_post = get_post( $attachment_id );
    $file_path = get_attached_file( $attachment_id );

    if( !$file_post || !$file_path || !file_exists( $file_path ) ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Logic for validating current user's access to this file...
    // Option A: check for user capability
    if( !current_user_can( 'required_capability' ) ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Option B: check against current user
    if( $current_user->user_login == "authorized_user" ) {
        $wp->query_vars['error'] = '404';
        return;
    }

    // Everything checks out, user can see this file. Simulate headers and go:
    header( 'Content-Type: ' . $file_post->post_mime_type );
    header( 'Content-Dispositon: attachment; filename="'. basename( $file_path ) .'"' );
    header( 'Content-Length: ' . filesize( $file_path ) );

    echo file_get_contents( $file_path );
    die(0);
}
add_action( 'wp', 'intercept_file_request' );

NB 此解决方案适用于 single-site 安装!这是因为 WordPress MU 已经通过 wp-includes/ms-files.php 在 sub-sites 中转发上传的文件请求。 WordPress MU 也有一个解决方案,但是它有更多的参与。

次佳解决方案

我最近有一个相关的问题和 wrote this article about it

我会假设下载是通过 WordPress 的媒体处理上传的,否则你有一个下载的附件 ID 。

解决方案纲要

  • 制作上传目录’secure'(在这个意义上,我只是指使用.htaccess 阻止任何尝试直接访问上传目录 (或其 sub-directory) 中的文件 – 例如通过 mysite.com/wp-content/uploads/conf/2012/09/myconfidentialfile.pdf)

  • 创建包含附件 ID 的下载链接 – 这通过 WordPress 检查用户查看附件允许/拒绝访问权限。

Caveats

  • 这使得.htaccess 提供安全性。如果这不可用/打开 (例如,nginx 服务器),那么你将不会有太多的安全性。您可以阻止用户浏览 uplods 目录。但直接访问将工作。

  • 按照上述。如果您需要绝对的安全性,则不应在分发中使用。如果你的具体设置是有效的 – 但一般来说,它是不能保证的。我的链接文章部分是为了解决这个问题。

  • 你会松开缩略图。阻止直接访问文件夹或 sub-folder 将意味着无法查看该文件夹中的文件的缩略图。我的链接文章部分尝试解决这个问题。

阻止直接访问

要在您的上传文件夹 (或子文件夹 – 所有保密材料必须位于此文件夹内的任何深度) 上执行此操作。放置一个.htaccess 文件与以下:

Order Deny,Allow
Deny from all

在下面我假设你将附加机密资料到帖子类型’client’ 。上传到 client-edit 页面上的任何媒体将存储在 uploads/conf/文件夹中

设置受保护的 uploads 目录的功能

function wpse26342_setup_uploads_dir(){

    $wp_upload_dir = wp_upload_dir();
    $protected_folder = trailingslashit($wp_upload_dir['basedir']) . 'conf';

    // Do not allow direct access to files in protected folder
    // Add rules to /uploads/conf/.htacess
    $rules = "Order Deny,Allown";
    $rules .= "Deny from all";

    if( ! @file_get_contents( trailingslashit($protected_folder).'.htaccess' ) ) {
            //Protected directory doesn't exist - create it.
        wp_mkdir_p( $protected_folder);
    }
    @file_put_contents( trailingslashit($protected_folder).'.htaccess', $rules );

     //Optional add blank index.php file to each sub-folder of protected folder.
}

上传保密材料

   /**
    * Checks if content is being uploaded on the client edit-page
    * Calls a function to ensure the protected file has the .htaccess rules
    * Filters the upload destination to the protected file
    */
    add_action('admin_init', 'wpse26342_maybe_change_uploads_dir', 999);
    function wpse26342_maybe_change_uploads_dir() {
        global $pagenow;

        if ( ! empty( $_POST['post_id'] ) && ( 'async-upload.php' == $pagenow || 'media-upload.php' == $pagenow ) ) {
                if ( 'client' == get_post_type( $_REQUEST['post_id'] ) ) {
                       //Uploading content on the edit-client page

                       //Make sure uploads directory is protected
                       wpse26342_setup_uploads_dir();

                       //Change the destination of the uploaded file to protected directory.
                       add_filter( 'upload_dir', 'wpse26342_set_uploads_dir' );
                }
        }

    }

完成后,上传的内容应该在 uploads/conf 内,并尝试使用浏览器直接访问它不应该。

下载内容

这很容易下载网址可以是 www.site.com?wpse26342download=5(其中 5 是上传的内容的附件 ID) 。我们使用它来标识附件,检查当前用户的权限并允许他们下载。

首先设置查询变量

/**
 * Adds wpse26342download to the public query variables
 * This is used for the public download url
 */
add_action('query_vars','wpse26342_add_download_qv');
function wpse26342_add_download_qv( $qv ){
    $qv[] = 'wpse26342download';
    return $qv;
}}

现在设置一个监听器 (可能) 触发下载…

add_action('request','wpse26342_trigger_download');
function wpse26342_trigger_download( $query_vars ){

        //Only continue if the query variable set and user is logged in...
    if( !empty($query_vars['wpse26342download']) && is_user_logged_in() ){

        //Get attachment download path
        $attachment = (int) $query_vars['wpse26342download'];
        $file = get_attached_file($attachment);

        if( !$file )
             return;

        //Check if user has permission to download. If not abort.
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='.basename($file));
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));

        ob_clean();
        flush();
        readfile($file);
        exit();
    }
    return $query_vars;
}

最后评论

上面的代码可能包含错误/语法错误,并且未经测试,并且您自己承担风险:) 。

下载网址可以使用’prettified’ 重写。如注释中所述,您可以在受保护文件夹的每个子节点中添加一个空白的 index.php,以防止浏览 – 但是这应该由.htaccess 规则阻止。

一个更安全的方法是将公共文件存储在公共目录之外。或者像 Amazon S3 这样的外部服务。对于后者,您需要生成一个有效的 URL 来从 Amazon 获取文件 (使用您的私钥) 。这两者都需要您的主机/第三方服务的一定程度的信任。

我会谨慎使用任何建议他们提供’protected downloads’ 的 plug-ins 。我没有发现任何提供足够的安全性。请不要这个解决方案的注意事项 – 我欢迎任何建议或批评。

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。