How to protect a file on sub-domain of main site f

2019-05-31 08:10发布

For example my main domain www.example.com which is a file sharing website where we keep files in sub domain like server1.example.com and server2.example.com to share with visitor. My question is how to protect a file on sub-domain (sub-domain might point to different server or host) direct from download without authentication in wordpress/mainsite.

My current code credit goes to http://wordpress.stackexchange.com/a/37743/12438

.htaccess file

RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]

SO my entire .htaccess code looks like

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# BEGIN THIS DL-FILE.PHP ADDITION
RewriteCond %{REQUEST_URI} ^.*wp-content/uploads/.*
RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]
# END THIS DL-FILE.PHP ADDITION

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

Options -Indexes

but it can Protect only a file inside wp-content/uploads/* here is dl-file.php

<?php
ob_start();
require_once('wp-load.php');
require_once ABSPATH . WPINC . '/formatting.php';
require_once ABSPATH . WPINC . '/capabilities.php';
require_once ABSPATH . WPINC . '/user.php';
require_once ABSPATH . WPINC . '/meta.php';
require_once ABSPATH . WPINC . '/post.php';
require_once ABSPATH . WPINC . '/pluggable.php';
wp_cookie_constants();
$discard = ob_get_clean();

is_user_logged_in() ||  auth_redirect();


list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);

$file = rtrim($basedir, '/') . '/' . (isset($_GET['file']) ? $_GET['file'] : '');
$file = realpath($file);

if ($file === FALSE || !$basedir || !is_file($file)) {
    status_header(404);
    die('404 &#8212; File not found.');
}

if (strpos($file, $basedir) !== 0) {
    status_header(403);
    die('403 &#8212; Access denied.');
}


$mime = wp_check_filetype($file);
if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
    $mime[ 'type' ] = mime_content_type( $file );
if( $mime[ 'type' ] )
    $mimetype = $mime[ 'type' ];
else
    $mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 );
header( 'Content-Type: ' . $mimetype ); // always send this
if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) )
    header( 'Content-Length: ' . filesize( $file ) );
$last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) );
$etag = '"' . md5( $last_modified ) . '"';
header( "Last-Modified: $last_modified GMT" );
header( 'ETag: ' . $etag );
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' );
// Support for Conditional GET
$client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false;
if( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )
    $_SERVER['HTTP_IF_MODIFIED_SINCE'] = false;
$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
// If string is empty, return 0. If not, attempt to parse into a timestamp
$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;
// Make a timestamp for our most recent modification...
$modified_timestamp = strtotime($last_modified);
if ( ( $client_last_modified && $client_etag )
    ? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) )
    : ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) )
    ) {
    status_header( 304 );
    exit;
}
// If we made it this far, just serve the file
readfile( $file );

2条回答
Root(大扎)
2楼-- · 2019-05-31 08:42

Step 1:

Add the following to the '.htaccess' of all sub domains.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s

RewriteCond %{QUERY_STRING} !key=(FgTyUeL)
RewriteRule ^(.*)$ http://www.example.com/?dwnld_file=server1/$1 [QSA,L]

In the above code, we are redirecting all the files to the main domain except if a post parameter set. For example, http://server1.example.com/file.pdf will be redirected to http://www.example.com/?dwnld_file=server1/file.pdf. Here server1 is your sub domain name in order to identify if multiple sub domains.

In the rewrite condition - RewriteCond %{QUERY_STRING} !key=(FgTyUeL), key and its value FgTyUeL should be confidential and feel free to change to more complex strings. We will use this to access the file in WordPress.

Step 2:

Add these to your theme's 'functions.php' of main domain.

/* init hook to check whether user logged-in and pass to download function */
add_action( 'init', 'file_init' );
function file_init() {
    if ($_REQUEST[ 'dwnld_file' ] != '') {
        if ( ! is_user_logged_in() ) { // if not logged-in
            auth_redirect(); //redirect to login page
            // wp_redirect( 'http://www.example.com/login' ); // or some other page
            exit;
        }
        else {
            $spliturl = explode("/",$_REQUEST[ 'dwnld_file' ]);
            $originalurl = "http://".$spliturl[0].".example.com/".substr($_REQUEST[ 'dwnld_file' ], strpos($_REQUEST[ 'dwnld_file' ], "/") + 1);                
            check_download_file( $originalurl ); // if logged-in pass file to download
        }
    }
}

/* function to download file */
function check_download_file( $url ) {
    $key='?key=FgTyUeL'; // secure post key same as .htaccess
    $file = basename($url);
    $mime = wp_check_filetype( $file ); 

    if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
        $mime[ 'type' ] = mime_content_type( $file );

    if( $mime[ 'type' ] )
    {           
        set_time_limit(0);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url.$key);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $curl_response = curl_exec($ch);

        curl_close($ch);
        header('Expires: 0'); // no cache
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
        header('Cache-Control: private', false);
        header('Content-Type: application/force-download');
        header('Content-Disposition: attachment; filename="' . basename($url) . '"');
        header('Content-Transfer-Encoding: binary');
        header('Content-Length: ' . strlen($curl_response)); // provide file size
        header('Connection: close');
        echo $curl_response;
        die();            
    }
}

The above functions are self-explanatory - we are checking WordPress authentication and if logged-in user, we will append the secure key and download the file using cURL. Please note that the secure key should be same as the one in the '.htaccess' file of your sub domain(s).

The above logic will work if file resides in any folder of your sub domain and even across different domains (but should be publicly accessible).

查看更多
Evening l夕情丶
3楼-- · 2019-05-31 09:06

Put protected files in a private folder outside of the publicly accessible area of your web site. Then, make a download page that takes a file name in the url or whatever. Then on that page, do the auth check:

is_user_logged_in() ||  auth_redirect();
// open file
// send headers
// echo contents
查看更多
登录 后发表回答