PDF files protection from external access. Accessible only to authenticated users. WordPress uploads directory
Asked Answered
D

4

7

I am running a website and I would like to protect all the PDF files inside the WordPress uploads folder from external access and hotlinking.

I am already using a user authentication to protect the posts attached to these files, but the user authentication doesn't protect the direct link to the PDF file or the indexing of these files from search engines.

I would prefer not to change the default uploads directory since the PDFs are over 1000 with random filenames and attached to various posts with different dates.

The site is hosted on a Debian VPS with Nginx, php5-fpm, and MariaDB.

So far, I have tested the following:

site.conf 1

location /wp-content/uploads/ {
    location ~* \.(pdf)$ {
        valid_referers blocked example.com *.example.com;
        if ($invalid_referer) {
            return 301 https://example.com/services/login-error.html;
        }
    }
}

site.conf 2

location /wp-content/uploads/ {
    location ~* \.(pdf)$ {
        valid_referers blocked example.com *.example.com;
        if ($invalid_referer) {
            deny all;
            return 403;
        }
    }
}

Unfortunately, none of the above configurations work as expected. They block the external access but they also redirect the authenticated user to either 403 or 301 errors.

Any help or suggestion would be appreciated.

Thanks.

Damarisdamarra answered 26/7, 2018 at 10:12 Comment(2)
You could use this to mask the URLs (wordpress.org/plugins/random-file-upload-names) to hide absolute files. You can debug this here too (wpza.net/wordpress-setup/…).Featherston
@Featherston Thanks for the link. Unfortunately this plugin doesn’t encrypt files that are already uploaded.Damarisdamarra
D
0

So, eventually what I found, after trying all the answers and more, was that while the site.conf #1 was working with the logged-in users for PDF files with URLs starting with https://, it was not working with previous uploads that used to have the http:// in the URL. I had to update the wp_posts table to https://example.com/wp-content/uploads/ and it was finally protecting (only) the PDF files from direct access.

Of course this is a rough workaround and keep in mind that this method will also protect PDF files that are otherwise publicly available.

Thanks for all the help.

Damarisdamarra answered 10/8, 2018 at 15:16 Comment(0)
B
0

The best way here is to use http://nginx.org/r/auth_request to determine, within a given location, whether the user is authenticated and access should be granted. You'd have to figure out the correct end-point for WordPress, though.

Another option is to move all the files to an http://nginx.org/r/internal location, and use X-Accel-Redirect HTTP Response Header Field from a new script handling the old location to which all the external links are made, to redirect only those clients that are authenticated to the internal location as mentioned above.

Brunk answered 28/7, 2018 at 16:53 Comment(1)
I will look into it. Thank you.Damarisdamarra
G
0

Instead of redirecting non-logged in users to error URL, pass the file path to a page - can be your home page. Add the following rule.

rewrite ^/wp-content/uploads/(.*)\.(?!js|css|png|jpe?g|gif)(.*)$ /?dwnld_file=$1.$2

Then check whether the user logged-in or not via WordPress and provide access if valid. Add the following to your theme's 'functions.php'.

//init hook
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( 'https://example.com/services/login-error.html' ); // or some other page
            exit;
        }
        else {
            check_download_file( $_REQUEST[ 'dwnld_file' ] ); // if logged-in pass file to download
        }
    }
}

//function to download file
function check_download_file( $file ) {
    $upload = wp_upload_dir();
    $file = $upload[ 'basedir' ] . '/' . $file;
    if ( !is_file( $file ) ) {
        status_header( 404 );
        die( 'File not found.' );
    }
    else {
        $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' ];        

            header( 'Content-type: ' . $mimetype );
            $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' );

            readfile( $file );
            die();
        }
    }
}

Hope this helps.

Gloom answered 30/7, 2018 at 8:21 Comment(3)
He wants to secure direct links to PDF and hide files from search engines.Writing
If posts were protected already then PDF links in those posts content also be protected and not indexed in search engines. He is asking how to protect direct PDF links.Gloom
Protected posts (login) don’t protect file links inside them by default.I will probably have to move files to another non public folder and set permissions but the problem is that I don’t know how to safely separate only the attachment PDFs and then how to update the DB correctly. I want to secure the direct links to the files from outside of my domain, while have the files only accessible to authenticated users. I want to avoid re uploading them.Damarisdamarra
D
0

So, eventually what I found, after trying all the answers and more, was that while the site.conf #1 was working with the logged-in users for PDF files with URLs starting with https://, it was not working with previous uploads that used to have the http:// in the URL. I had to update the wp_posts table to https://example.com/wp-content/uploads/ and it was finally protecting (only) the PDF files from direct access.

Of course this is a rough workaround and keep in mind that this method will also protect PDF files that are otherwise publicly available.

Thanks for all the help.

Damarisdamarra answered 10/8, 2018 at 15:16 Comment(0)
E
0

I've done it from .htaccess reading wordpress_logged_in cookie.

Protect all files in uploads folder:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP_COOKIE} !.*wordpress_logged_in.*$ [NC]
    RewriteCond %{REQUEST_URI} ^(.*?/?)wp-content/uploads/.* [NC]
    RewriteRule . http://%{HTTP_HOST}%1/wp-login.php?redirect_to=%{REQUEST_URI} [L,QSA]
</IfModule>

Protect only some files extension:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP_COOKIE} !.*wordpress_logged_in.*$ [NC]
    RewriteCond %{REQUEST_URI} ^(.*?/?)wp-content/uploads/.*\.(?:gif|png|jpe?g|pdf|txt|rtf|html|htm|xlsx?|docx?|mp3|mp4|mov)$ [NC]
    RewriteRule . http://%{HTTP_HOST}%1/wp-login.php?redirect_to=%{REQUEST_URI} [L,QSA]
</IfModule>

Read more here.

Enounce answered 27/7, 2021 at 22:14 Comment(2)
Hi! Thank you. I am using NGINX in this case but I believe there is a way to convert the rules you shared above to make them work similarly for NGINX.Damarisdamarra
Hi, from what i've read, you achieved it. Can you tick your question as resolved ? thanksHolusbolus

© 2022 - 2024 — McMap. All rights reserved.