如何将附件下载限制为特定用户?


12

我有一个非常具体的用例,该站点是为律师建立的,他的每个客户都可以登录到自己的“特定页面/门户”(自定义帖子类型),而无法访问wp-admin等。(我创建了所有前端的登录/注册/配置文件编辑页面)。在此页面/门户中,律师将留下消息和文件供客户下载,现在从理论上讲,一个客户可以猜测(或者如果知道另一位客户的文件)其他文件名并下载,从而造成隐私/安全性问题/机密材料等

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

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

谢谢!


您找到了解决方案吗?
brasofilo 2012年

@brasofilo,否。–
Amit

Answers:


6

需要发生的是,您需要通过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该解决方案适用于单站点安装!这是因为WordPress MU已经通过转发了子站点中的上传文件请求wp-includes/ms-files.php。也有适用于WordPress MU的解决方案,但是涉及的更多。


1
您好,我没有看到您逐步挂接此函数intercept_file_request或在任何地方调用该函数,该函数如何触发?
Bobz

好点了,应该挂上了wp,我已经更新了示例。
本多

3

我最近遇到了一个相关问题,并撰写了有关此问题的文章

我假定下载是通过WordPress的媒体处理上传的,否则您将拥有下载的附件ID。

解决方案概要

  • 使上传目录“安全”(在这个意义上,我只是意味着使用.htaccess以阻止任何企图在上传目录直接文件的访问(或其子目录) -例如,通过mysite.com/wp-content/uploads/conf/2012/09/myconfidentialfile.pdf
  • 创建一个包含附件ID的下载链接-这将通过WordPress检查用户的权限,以查看附件允许/拒绝访问。

注意事项

  • 这利用.htaccess提供安全性。如果此功能不可用/未启用(例如nginx服务器),那么您将不会获得太大的安全性。您可以防止用户浏览 uplods目录。但是直接访问将起作用。
  • 如上所述。如果您需要绝对的安全性,则不应在分发中使用它。如果您的特定设置可行,那就很好-但总的来说,不能保证。我的链接文章部分试图解决此问题。
  • 您将松开缩略图。阻止直接访问文件夹或子文件夹将意味着无法查看该文件夹中文件的缩略图。我的链接文章部分试图解决此问题。

阻止直接访问

为此,请在您的上载文件夹(或子文件夹-所有机密材料必须位于该文件夹内的任何深度)内。.htaccess使用以下文件放置文件:

Order Deny,Allow
Deny from all

在下文中,我假设您将在“客户”类型后附加机密材料。在客户端编辑页面上上传的所有媒体都将存储在该uploads/conf/文件夹中

设置受保护的上传目录的功能

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,Allow\n";
    $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;
}

最后评论

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

可以使用重写“美化”下载URL。如评论中所述,您可以index.php在受保护的文件夹的每个子文件夹中添加一个空格以防止浏览-但这仍应由.htaccess规则来防止。

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

我会警惕使用任何暗示它们提供“受保护的下载”的插件。我还没有找到能够提供足够安全性的产品。也请不要对此解决方案提出警告-我欢迎任何建议或批评。


1

可能您已经知道此技巧,该代码将检查当前登录用户的用户名,如果匹配,它将显示该文件的下载链接,否则将不显示任何内容。

这是代码:

<?php 
    global $current_user;
    get_currentuserinfo();

    if ( 'username' == $current_user->user_login ) {
        echo 'Download Link';
    } else {
        // nothing
    }
?>

但是,这不是一个好方法,因为文件存储在服务器上,所以具有链接的任何人都可以下载该文件。


0

我认为此信息是机密的,因此,除了隐藏指向文件的链接之外,您还希望使他们真正无法被网络上的任何人访问,即使他们猜测的是URL,除非该用户具有明确的下载许可文件。

考虑将文件安全地存储在Amazon S3上,然后在满足正确的安全检查(即用户已登录到您的网站并说出自己的身份)的情况下,为文件提供预签名(时间受限)URL。

有一个非常好的AWS开发工具包,可以很直接地做到这一点。

您需要研究的是如何将通过WP上传接口上传的文件发送到S3中,或者构建自己的上载器

另一种选择是也可以考虑WP电子商务的代码。它们提供软件文件(例如MP3)的安全下载。我相信,使用购买时每个用户生成的加密密钥,文件会转换为哈希。这需要一些破译才能了解它是如何工作的,但是该过程对于该插件而言并不是唯一的,因此可以(在某个地方)使用其他示例。


0

我认为对文件进行加密是类似于上述答案的方法。Wordpress.org上有一个插件,可让您保护下载。 http://wordpress.org/extend/plugins/download-protect/ 您也可以使用亚马逊服务或Google驱动器。也有很多服务提供受保护的下载,例如保管箱。


晦涩难懂的安全性是不好的方法。任何人都可以看到http请求并以这种方式获取url。
mulllhausen 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.