问题描述
我有一个非常具体的用例,该网站为律师建立,每个客户都可以登录自己的’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 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。