Tell me more ×
WordPress Answers is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I use wordpress for a private site where users upload files. I use the "Private WordPress" to prevent access in to the site if the user is not logged in.

I would like to do the same to the files uploaded in the uploads folder.

So if a user its not logged in they wont be able to access to : https://xxxxxxx.com/wp-content/uploads/2011/12/xxxxxxx.pdf if they try to access but they are not logged then they should be redirected to login page for example.

I found a plugin called private files but last time updated was in 2009 and it dont seems to work on my wordpress.

Anyone know any method? Hotlinking method will be enough to protect this?

I also found this method :

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^.*uploads/private/.*
RewriteCond %{HTTP_COOKIE} !^.*wordpress_logged_in.*$ [NC]
RewriteRule . /index.php [R,L]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

But then any user that replicate the cookie could pass this right? Regards

share|improve this question
1  
Any reason why you can't use a different upload directory, like one outside of the site root? – One Trick Pony Dec 22 '11 at 17:11
Not really but i already got tons of files attached to posts in that directory, i dont mind on moving all around if i can find a proper solution – chifliiiii Dec 22 '11 at 17:37

2 Answers

up vote 17 down vote accepted
+50

Only checking if the cookie exists, is not much of a strict protection.

To get a stronger protection, you can pass or "proxy" all requests to the uploaded folder (exemplary uploads in the following example) through a php script:

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

All requests to uploaded files (which includes images in posts) would go to dl-file.php which then can do verify if the user is logged in or not.

If the user is not logged in, your sites login-form will be shown. After the user logged in, she will get redirected back to the file and can download it now.

Exemplary dl-file.php.

Something similar can be found in \wp-includes\ms-files.php in your wordpress installation, but that one is for multisite and w/o the login check and redirects.

Depending on how much traffic you have, it could be wise to better integrate this with your server, e.g. X-Accel-Redirect or X-Sendfile headers.

share|improve this answer
Thanks,i will test it out and let you know – chifliiiii Jan 3 '12 at 18:45
Perfect solution, you are a champ! – chifliiiii Jan 5 '12 at 18:35
how do you adjust dl-file.php if i want to store files into a subdirectory such as wp-content/uploads/secure ? – user11958 Jan 11 '12 at 22:54

You can also write an plugin, about the init Hook and the get-value $_GET[ 'file' ]; if the user has this get-value, jump in a function to check the rights for access on the files; maybe files on different posts with a checkbox to the posts in a Meta Box.

add_action( 'init', 'fb_init' );
function fb_init() {
    // this in a function for init-hook
    if ( '' != $_GET[ 'file' ] ) {
        fb_get_file( $_GET[ 'file' ] );
    }
}

the function get_file()

    function fb_get_file( $file ) {

        $upload     = wp_upload_dir();
        $the_file   = $file; 
        $file       = $upload[ 'basedir' ] . '/' . $file;
        if ( !is_file( $file ) ) {
            status_header( 404 );
            die( '404 &#8212; File not found.' );
        }
        else {
            $image = get_posts( array( 'post_type' => 'attachment', 'meta_query' => array( array( 'key' => '_wp_attached_file', 'value' => $the_file ) ) ) );
            if ( 0 < count( $image ) && 0 < $image[0] -> post_parent ) { // attachment found and parent available
                if ( post_password_required( $image[0] -> post_parent ) ) { // password for the post is not available
                    wp_die( get_the_password_form() );// show the password form 
                }
                $status = get_post_meta( $image[0] -> post_parent, '_inpsyde_protect_content', true );

                if ( 1 == $status &&  !is_user_logged_in() ) {
                    wp_redirect( wp_login_url( $upload[ 'baseurl' ] . '/' . $the_file ) );
                    die();
                }
            }
            else {
                // not a normal attachment check for thumbnail
                $filename   = pathinfo( $the_file );
                $images     = get_posts( array( 'post_type' => 'attachment', 'meta_query' => array( array( 'key' => '_wp_attachment_metadata', 'compare' => 'LIKE', 'value' => $filename[ 'filename' ] . '.' . $filename[ 'extension' ] ) ) ) );
                if ( 0 < count( $images ) ) {
                    foreach ( $images as $SINGLEimage ) {
                        $meta = wp_get_attachment_metadata( $SINGLEimage -> ID );
                        if ( 0 < count( $meta[ 'sizes' ] ) ) {
                            $filepath   = pathinfo( $meta[ 'file' ] );
                            if ( $filepath[ 'dirname' ] == $filename[ 'dirname' ] ) {// current path of the thumbnail
                                foreach ( $meta[ 'sizes' ] as $SINGLEsize ) {
                                    if ( $filename[ 'filename' ] . '.' . $filename[ 'extension' ] == $SINGLEsize[ 'file' ] ) {
                                        if ( post_password_required( $SINGLEimage -> post_parent ) ) { // password for the post is not available
                                            wp_die( get_the_password_form() );// show the password form 
                                        }
                                        die('dD');
                                        $status = get_post_meta( $SINGLEimage -> post_parent, '_inpsyde_protect_content', true );

                                        if ( 1 == $status &&  !is_user_logged_in() ) {
                                            wp_redirect( wp_login_url( $upload[ 'baseurl' ] . '/' . $the_file ) );
                                            die();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        $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 );
        die();
    }

you can also add an custom url for files via hook generate_rewrite_rules

add_filter( 'generate_rewrite_rules', 'fb_generate_rewrite_rules' );

function fb_generate_rewrite_rules( $wprewrite ) {

        $upload                     = wp_upload_dir();
        $path                       = str_replace( site_url( '/' ), '', $upload[ 'baseurl' ] );
        $wprewrite -> non_wp_rules  = array( $path . '/(.*)' => 'index.php?file=$1' );
        return $wprewrite;
    }
share|improve this answer

protected by toscho Aug 2 '12 at 20:36

This question is protected to prevent "thanks!", "me too!", or spam answers by new users. To answer it, you must have earned at least 10 reputation on this site.

Not the answer you're looking for? Browse other questions tagged or ask your own question.