Secure Folders in WordPress Root
-
Is there any way we can add the CAS authentication/security to folders in WordPress root? (e.g., /video, /pdf, /images, /uploads, etc.) We really need to secure the entire site and all of its assets. Not just login. In other words, we don’t want anyone to access ‘www.website.com/video/sample_video.mp4’ straight away if they are not authenticated and logged into the site. It should redirect to authentication before accessing. Same is true for the wp-content/uploads folder and everything therein. Please advise.
-
add the CAS authentication/security to folders in WordPress root?
Authorizer provides an alternative to user authentication via local user account by substituting CAS (or Google or LDAP). Limiting access to assets at the folder level is a different type of implementation that Authorizer does not do out of the box.
Same is true for the wp-content/uploads folder and everything therein.
This would mean that no uploaded images could be seen if added to a published page, it that your intent?
My intent is to ONLY show uploaded images (and any other assets) after the user has been authenticated with Authorizer. (CAS, LDAP, etc.) So, in theory, the site is not Public. It is only visible to those who have been logged in through Authorizer. Seems like a pretty great feature to complement the Authorizer plugin code base. I would, personally, pay a premium for such a feature and I think it would make the plugin that much more valuable. @mmcglynn are you a contributor?
We’ve done this more robust version of a “private WordPress site” before (it was a site for HR documents that could potentially contain privileged information–so lots of PDFs). I can walk you through what we did, but it does require some coordination that’s larger than the scope of this plugin.
1) First off, configure Authorizer to only allow logged in users to see the site. This will prevent anonymous access to anything routed through WordPress, including the REST API (e.g., /wp-json/wp/v2/posts).
2) For completeness (in case things change in the future), use the WordPress-suggested way to prevent anonymous access to the REST API:
https://developer.www.ads-software.com/rest-api/using-the-rest-api/frequently-asked-questions/#require-authentication-for-all-requests3) Now it’s time to prevent anonymous access to static resources. This requires configuration at the web server level, so the info below only applies to Apache.
3a) Place an .htaccess file in your /wp-content/uploads directory:
# Block public access to file uploads directly. RewriteEngine On RewriteBase /wp-content/uploads RewriteRule . /DirectAccessToFilesDenied Options -Indexes
This basically redirects all requests to something in the uploads directory to some path that doesn’t exist. WordPress’s own .htaccess file tells the web server to handle requests like this, so this will eventually render your WordPress 404 template if we stopped here.
3b) Hook into
parse_request
to examine all WordPress requests, and do something for the ones requesting a file from the uploads directory. We basically do anis_user_logged_in()
check first, and if it fails, send the request on, which will ultimately render the 404 template. If Authorizer is configured to prevent access to anonymous users, at this point you can choose to have it show the access denied message, or redirect to wp-login.php. Moving on, for users that pass theis_user_logged_in()
check, we can now tell PHP to do what the web server would have done: just output the headers and raw file content to the client, and then exit. Here’s what that hook might look like:// Require authentication for direct access to /wp-content/uploads directory. // NOTE: This feature requires the following .htaccess file to be present // in the /wp-content/uploads directory, in order to redirect all requests // to WordPress. We intercept these requests here, and if the current user // is logged in, show it to them. Otherwise load the 404 template. // .htaccess file: // // # Block public access to file uploads directly (REST endpoint will be the only way to grab them). // RewriteEngine On // RewriteBase /wp-content/uploads // RewriteRule . /DirectAccessToFilesDenied // Options -Indexes // add_action( 'parse_request', function ( $query ) { // If user isn't logged in, fall through (will go to the 404 template). if ( ! is_user_logged_in() ) { return; } // Check if this is a request for something in /wp-content/uploads. $upload_dir = wp_upload_dir(); $relative_upload_dir = str_replace( ABSPATH, '', $upload_dir['path'] ) . '/'; if ( strpos( $query->request, $relative_upload_dir ) === 0 ) { // Don't do anything if REQUEST_URI contains ../ // (possible attempt to bypass uploads directory). if ( strpos( $query->request, '../' ) !== false ) { return $type; } // Get attachment file from local filesystem (avoid .htaccess checks). $file_path = ABSPATH . ltrim( $query->request, '/' ); $detected_type = wp_check_filetype( $file_path, get_allowed_mime_types() ); $mime_type = array_key_exists( 'type', $detected_type ) ? $detected_type['type'] : 'text/plain'; $file_time = filemtime( $file_path ); $file_contents = file_get_contents( $file_path ); // Craft the http response containing the file data (actually just // short-circuit by outputing the headers and file data). header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $file_time ) . ' GMT' ); header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', $file_time + 86400*365 ) . ' GMT' ); header( 'Content-Length: ' . filesize( $file_path ) ); header( 'Content-Type: ' . $mime_type ); header( 'Content-Disposition: inline; filename="' . addslashes( basename( $file_path ) ) . '"' ); echo $file_contents; exit; } });
4) That should be it. Feel free to poke holes in that!
@figureone Thank you so very much for this! What a huge help! One question, when I add the .htaccess to wp-content/uploads it seems to break the CSS. Am I missing something obvious here? I’m also a little fuzzy on where to hook into the parse_request. There are many places when I grep recursively from root. Does 3b replace 3a or do they work in tandem? It appears that 3a is commented out in the example under 3b. Is the add_action code added to .htaccess as well? Thanks again!
Re: css, if you have any other plugins that generate inline css, they often put the css files in the uploads directory. So you may have to make multiple RewriteRule entries to be more specific (e.g., *.pdf, *.zip, etc.). More details here:
https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriteruleRe: where to hook in, that’s usually done in your theme. functions.php is the typical entry point for theme hooks, though many themes are more complicated. If you’re using a paid theme, you can often create a child theme and put all your customizations in there. There are a bunch of tutorials out there, here’s one:
https://wpsites.net/web-design/add-custom-hooks-to-theme/Re: 3b and 3a, yes, they work in tandem. I just put the .htaccess in the comment in 3b because that’s where I have it in my code (just as documentation for myself so I don’t forget about the .htaccess file). The .htaccess file contains Apache-specific directives, while the rest of the code sample is PHP, so they don’t mix. The PHP code snippet should ideally go in your theme.
- The topic ‘Secure Folders in WordPress Root’ is closed to new replies.