Skip to content

Instantly share code, notes, and snippets.

@ideadude
Created September 15, 2021 15:49
Show Gist options
  • Save ideadude/01d50d496612e6d378cae509c41b52b9 to your computer and use it in GitHub Desktop.
Save ideadude/01d50d496612e6d378cae509c41b52b9 to your computer and use it in GitHub Desktop.
Lock down non-WordPress files in *multiple private directories* with Paid Memberships Pro.
<?php
/*
This code handles loading a file from one or more protected directories.
This is a variation of the code presented here:
https://www.paidmembershipspro.com/locking-non-wordpress-files-folders-paid-memberships-pro/
(!) Be sure to change the $protected_directories arary below.
(!) Add this code to a code snippet or custom plugin.
(!) You should have a corresponding bit of code in your Apache .htaccess file
to redirect files to this script. You need one line per protected dir.
###
# BEGIN protected folder lock down
<IfModule mod_rewrite.c>
RewriteBase /
RewriteRule ^private1/(.*)$ /index.php?pmpro_getfile=private1/$1 [L]
RewriteRule ^private2/(.*)$ /index.php?pmpro_getfile=private2/$1 [L]
</IfModule>
# END protected folder lock down
###
*/
define( 'PROTECTED_DIR', 'private1' ); //change this to the name of the folder to protect
function my_pmpro_getfile() {
if( isset( $_REQUEST['pmpro_getfile'] ) ) {
global $wpdb;
/*
Define directories and the levels with access.
If the levels array is empty, any member will have access.
Directories are expected to be at the webroot.
CHANGE THIS!
*/
$protected_directories = array(
'private1' => array(), // any member can access
'private2' => array( 2,3 ), // levels 2 and 3 can access
);
// Prevent loops when redirecting to .php files.
if( ! empty( $_REQUEST['noloop'] ) ) {
status_header( 500 );
die( 'This file cannot be loaded through the get file script.' );
}
$uri = $_REQUEST['pmpro_getfile'];
if( ! empty( $uri ) && $uri[0] == '/' ) {
$uri = substr( $uri, 1, strlen( $uri ) - 1);
}
/*
Remove ../-like strings from the URI.
Actually removes any combination of two or more ., /, and \.
This will prevent traversal attacks and loading hidden files.
*/
$uri = preg_replace( "/[\.\/\\\\]{2,}/", "", $uri );
// Make sure this file is in one of our protected directories.
if( strpos( $uri, "/" ) !== false ) {
$parts = explode( "/", $uri );
$basedir = $parts[0];
} else {
$basedir = '';
}
if ( ! in_array( $basedir, array_keys( $protected_directories ) ) ) {
header( 'HTTP/1.1 503 Service Unavailable', true, 503 );
die( "HTTP/1.1 503 Service Unavailable" );
}
// Figure out the filename on the server.
$filename = ABSPATH . $uri;
$pathParts = pathinfo($filename);
// Remove params from the end.
if( strpos( $filename, "?" ) !== false ) {
$parts = explode( "?", $filename );
$filename = $parts[0];
}
// Add index.html if this is a directory.
if( is_dir( $filename ) ) {
$filename .= "index.html";
}
// Only checking if the file is pulled from outside the admin.
if( ! is_admin() ) {
// Check for access based on the directory accessed.
if( ! pmpro_hasMembershipLevel( $protected_directories[$basedir] ) ) {
//header('HTTP/1.1 503 Service Unavailable', true, 503);
//echo "HTTP/1.1 503 Service Unavailable";
if ( ! is_user_logged_in() ) {
wp_redirect( wp_login_url( home_url( $uri ) ) );
} else {
wp_redirect( home_url() );
}
exit;
}
}
// Get the mimetype.
require_once( PMPRO_DIR . '/classes/class.mimetype.php' );
$mimetype = new pmpro_mimetype();
$file_mimetype = $mimetype->getType( $filename );
//in case we want to do something else with the file
do_action( 'pmpro_getfile_before_readfile', $filename, $file_mimetype );
// If file is not found, die
if( ! file_exists( $filename ) ) {
status_header( 404 );
nocache_headers();
die( 'File not found.' );
}
//if blacklistsed file type, redirect to it instead
$basename = basename( $filename );
$parts = explode( '.', $basename );
$ext = strtolower( $parts[count( $parts ) - 1] );
//build blacklist and allow for filtering
$blacklist = array( "inc", "php", "php3", "php4", "php5", "phps", "phtml" );
$blacklist = apply_filters( "pmpro_getfile_extension_blacklist", $blacklist );
//check
if( in_array( $ext, $blacklist ) ) {
//add a noloop param to avoid infinite loops
$uri = add_query_arg( "noloop", 1, $uri );
//guess scheme and add host back to uri
if( is_ssl() ) {
$uri = "https://" . $_SERVER['HTTP_HOST'] . "/" . $uri;
} else {
$uri = "http://" . $_SERVER['HTTP_HOST'] . "/" . $uri;
}
wp_redirect( $uri );
exit;
}
require_once( PMPRO_DIR . '/classes/class.mimetype.php' );
//okay show the file
header( "Content-type: " . $file_mimetype );
readfile( $filename );
exit;
}
}
add_action( 'init', 'my_pmpro_getfile' );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment