Simple Membership Plugin › Forums › Simple Membership Plugin › Protecting Files/Media
Tagged: nginx
- This topic has 9 replies, 5 voices, and was last updated 1 year ago by
admin.
-
AuthorPosts
-
November 2, 2021 at 1:48 am #23225
Eldon McGuinness
ParticipantI wanted to protect certain posts, pages, and files/media and decided to give it a try using categories. To that end, here is how I am able to achieve it. This is based on the idea from hakre @ stackexchange
First, you’re going to need some way to redirect downloads to a PHP file that can check authorization and either direct the user to login OR prompt the download to happen.
.htaccess [Root of WordPress site folder]
RewriteCond %{REQUEST_FILENAME} -s RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]dl-file.php [Root of WordPress site folder]
<?php // Using Simple Membership Plugin to verify the user has access to the file function can_access_attachment($id) { $auth = SwpmAuth::get_instance(); $protected = SwpmProtection::get_instance(); //Check if this is a protected post. if (!$protected->is_protected($id)) { return true; } //Check if the user is logged in. if(!$auth->is_logged_in()){ return false; } //Check if the account is expired if ($auth->is_expired_account()){ return false; } //Check if this user's membership level has access to this post $permission = SwpmPermission::get_instance($auth->get('membership_level')); if(!$permission->is_permitted($id)) { return false; } //All checks have passed. Show this post to the user. return true; } require_once('wp-load.php'); if ( !is_user_logged_in() || !SwpmMemberUtils::is_member_logged_in() ){ auth_redirect(); } list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL); list($baseurl) = array_values(array_intersect_key(wp_upload_dir(), array('baseurl' => 1)))+array(NULL); $filename = basename($_GET['file']); $file_path = rtrim($basedir,'/').'/'.str_replace('..', '', isset($_GET[ 'file' ])?$_GET[ 'file' ]:''); $url = rtrim($baseurl,'/').'/'.str_replace('..', '', isset($_GET[ 'file' ])?$_GET[ 'file' ]:''); $id = attachment_url_to_postid( $url ); if (can_access_attachment( $id )) { if (!$baseurl || !$basedir || !is_file($file_path)) { status_header(404); die('404 — File not found.'); } $mime = wp_check_filetype($file_path); if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) ) $mime[ 'type' ] = mime_content_type( $file_path ); if( $mime[ 'type' ] ) $mimetype = $mime[ 'type' ]; else $mimetype = 'image/' . substr( $file_path, strrpos( $file_path, '.' ) + 1 ); header( 'Content-Type: ' . $mimetype ); // always send this if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) ) header( 'Content-Length: ' . filesize( $file_path ) ); $last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file_path ) ); $etag = '"' . md5( $last_modified ) . '"'; header( "Content-Disposition: attachment; filename=$filename" ); 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_path ); }else{ auth_redirect(); } ?>functions.php
<?php defined('ABSPATH') or die("Please don't run this script."); function pages_and_media_with_category_and_tag_register(){ /*add categories and tags to pages*/ register_taxonomy_for_object_type('category', 'page'); register_taxonomy_for_object_type('post_tag', 'page'); register_taxonomy_for_object_type('category', 'attachment'); register_taxonomy_for_object_type('post_tag', 'attachment'); } add_action( 'init', 'pages_and_media_with_category_and_tag_register'); function pages_and_media_with_category_and_tag_register_pre_get( $query ) { if ( is_admin() || ! $query->is_main_query() ) { return; } /*view categories and tags archive pages */ if($query->is_category && $query->is_main_query()){ $query->set('post_type', array( 'post', 'page', 'attachment')); } if($query->is_tag && $query->is_main_query()){ $query->set('post_type', array( 'post', 'page', 'attachment')); } } add_action( 'pre_get_posts', 'pages_and_media_with_category_and_tag_register_pre_get' );The functions.php edit allows you to have categories for pages and media as well as posts.
With this setup you can set categories on all three and protect files from external linking. Let me know if you find any issues with this or bugs.
November 3, 2021 at 4:16 am #23233mbrsolution
ModeratorHi, thank you for sharing your solution. I am sure others will find your solution helpful.
Kind regards.
November 3, 2021 at 8:54 am #23239admin
KeymasterThank you. I will add a reference to this on our following page:
https://simple-membership-plugin.com/user-submitted-tweaks-simple-membership/December 4, 2021 at 11:46 pm #23340Dan
ParticipantHas anyone had any luck with getting this to work on nginx? Running nginx and php8.0 I change the nginx config to match the rewrite rule for the htaccess file
I triedlocation / { if (-e $request_filename){ rewrite ^/wp-content/uploads/(.*)$ /dl-file.php?file=$1 last; } }and
location ~* /(?:uploads|files)/* { rewrite /wp-content/uploads/(.*)$ /dl-file.php?file=$1 last; }And only get dl-file.php downloaded when trying to access the media file as if the FastCGI server isn’t running, however it is and WordPress itself isn’t having any issues.
December 5, 2021 at 1:58 am #23341Dan
ParticipantSo in my case for nginx I wrote this for my site config. This will block someone from having direct access to the file and they cannot download it, and they will only be able to access it if the website serves them the post (so if it’s a member only post, only members can see the file). For now this is the functionality I am looking for, and it only needs the config to be edited without the need for additional scripts.
# Hotlink protection location ~ ^/wp-content/uploads/(.*) { valid_referers YOURSITEHERE.com www.YOURSITEHERE.com; if ($invalid_referer) { return 403; } }Hope someone else finds it useful
December 5, 2021 at 6:51 am #23343Eldon McGuinness
ParticipantHas anyone had any luck with getting this to work on nginx?
Yes, I wrote the original using NGINX, I would need to check and see what version of PHP, but I think it was 7.x. Though, that should not matter.# Hotlink protection location ~ ^/wp-content/uploads/(.*) { valid_referers YOURSITEHERE.com www.YOURSITEHERE.com; if ($invalid_referer) { return 403; } }This would only prevent them from “hot-linking” to the file, it would not prevent someone from inserting a link into the page or faking the headers and then downloading the file. This really is not a good solution for protecting files. Glad it works for you, but you might want to consider something more robust.
December 13, 2021 at 2:47 am #23379Dan
ParticipantAfter a server restart it seems to be working fine now with PHP8.1
Thanks!
I am however not able to preview the file in the media library when logged in as admin, though I think that has more to do with this plugin as I am not able to see the posts as well, only users with the proper membership tag are bale to see, but admins are not. I’ll play with the settings.December 13, 2021 at 3:55 am #23380mbrsolution
Moderator@Dan, please check the following documentation if you want to give admin access to protected posts and pages.
https://simple-membership-plugin.com/giving-wordpress-admin-user-access-protected-content/
Thank you.
March 28, 2022 at 10:14 pm #23755Faraz
ParticipantI tried implementing this solution, but it made it so that every item in the media library failed to load, except for any item I explicitly defined content restrictions for. Any file that did not have protection assigned failed to load at all. So, a PDF that I defined content restrictions for, would show up restricted for non-members, and download correctly for members. But anything that had no protection on it, would fail to load for non-members. If you were logged in the site loaded normally. Any way to make it load normally for non-members?
May 10, 2025 at 3:22 am #30370admin
KeymasterAdding to this topic, we now also have the following integration that can be used to protect media files:
-
AuthorPosts
- You must be logged in to reply to this topic.