1

I have an apache config where I whitelist IPs to a non-WordPress subfolder like this:

<Directory /var/www/html/link>
Order allow,deny
Require all granted

#Our Network
Allow from ip-address/22
Allow from ip-address/24
Allow from ip-address/24
Allow from ip-address/24
#and even longer list of IPs and other folders
</Directory>

I would also like to allow people who don't belong to this IP block but have user account. Is there any way to do this?

5
  • Though not secure, hypothetically, you can drop a custom cookie on login and then whitelist it using mod_rewrite cookie check. Probably can find what you're looking for here: stackoverflow.com/a/12941037/978430 Feb 6, 2017 at 20:44
  • Thanks for the pointer. That's a solution I have already looked into. The issues I had with it seem to require one IP at a time if I had an IP block like this 103.197.152.0/22. I have about 20 IP blocks that's more than 20k IPs to add. Also the ip adding examples shows this way ^123.45.67.89$ [OR], shouldn't it be like ^123\.45\.67\.89$ [OR], because all the other examples shows the later and I don't know which one is the right one. If I could just add ip blocks then I should be good to go.
    – bran
    Feb 6, 2017 at 22:36
  • When you said logged in users, did you mean logged in with your WordPress site? or does those subdirectories have some other login script/app?
    – Fayaz
    Feb 7, 2017 at 7:51
  • I meant to say wordpress users.
    – bran
    Feb 7, 2017 at 7:54
  • ASK that at stackoverflow. Not a WP-specific question i think..
    – T.Todua
    Feb 7, 2017 at 14:10

3 Answers 3

3
+50

Simple & Fast but not 100% secure method:

This method is not 100% secure, but for most users (like 99%), it'll work.

Here, basically you'll use a .htaccess file inside the restricted directory, say: /var/www/html/link with the following CODE:

    SetEnvIf Cookie "^.*wordpress_logged_in.*$" WP_LOGGED_IN=maybe

    Order allow,deny
    Require all granted

    # Our Network
    #Allow from ip-address/22
    #Allow from ip-address/24
    #Allow from ip-address/24
    #Allow from ip-address/24
    # and even longer list of IPs

    # Perhaps WordPress is logged in according to cookie: works for trivial cases, but not 100% secure
    Allow from env=WP_LOGGED_IN

So basically, instead of apache config, you'll put the allowed IP networks in this .htaccess file (without the <Directory> block), which will also check the WordPress login cookie using SetEnvIf directive. If the IP is not in the match & login cookie isn't found either, with this CODE the remaining visitors will be denied access.

No change in the .htaccess file of the root web directory is necessary.

Secure method:

If you need 100% secure method, then it's best to do it with PHP.

First remove all existing IP restrictions (for the corresponding directory) from the apache config file.

Then, use the following CODE in the .htaccess file of your root web directory.

    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteBase /

        RewriteRule ^link/access\.php$ - [L]
        RewriteRule ^link/.*$ /link/access.php [L]

        # Your remaining rewrite rules for WordPress
    </IfModule>

Then in /var/www/html/link/access.php file, use the following PHP CODE:

<?php

    function network_match( $ip, $networks ) {
        foreach( $networks as $network ) {
            $net_parts = explode( '/', $network );

            $net = $net_parts[0];
            if( count( $net_parts ) === 2 ) {
                $netmask = $net_parts[1];
                if( ( ip2long( $net ) & ~ ( ( 1 << ( 32 - $netmask ) ) - 1 ) ) === ( ip2long( $ip ) & ~ ( ( 1 << ( 32 - $netmask ) ) - 1 ) ) ) {
                    return true;
                }
            }
            else if( $ip === $net ) {
                return true;
            }
        }
        return false;
    }

    $networks = array(
        // Put your allowed networks HERE
        "192.168.0.0/24",
        "127.0.0.1"
    );

    $allowed = false;
    if( network_match( $_SERVER['REMOTE_ADDR'], $networks ) ) {
        $allowed = true;
    }
    else {
        // assuming WordPress is installed one directory above
        // if not, then change accordingly
        require_once dirname( dirname( __FILE__ ) ) . "/wp-load.php";
        if( is_user_logged_in() ) {
            $allowed = true;
        }
    }

    if( $allowed ) {
        // HERE you SERVE THE REQUESTED FILE WITH PHP
        echo "You are allowed";
    }
    else {
        die("Access denied!");
    }

As it is now, this CODE basically internally rewrites all the requests to /var/www/html/link/ directory to /var/www/html/link/access.php file & it checks IP access permission & WordPress login.

You may modify this PHP CODE (in the line that says: // HERE you SERVE THE REQUESTED FILE WITH PHP) to serve requested files from the link directory.

You may check this post to server files from PHP CODE.

3
  • I really like the first part of the answer, while not 100% secure, there is some elegance to it. Would it be possible to add an option where user gets redirected to another page like RewriteRule . /pro/ [R,L] if user is not logged in?
    – bran
    Feb 13, 2017 at 14:35
  • 1
    nevermind, got it: ErrorDocument 403 http://url/pro/
    – bran
    Feb 13, 2017 at 14:54
  • 1
    From your question I had the inclination that you might like that simpler solution, that's why I added it first. It'll work for the use case of many other people as well. However, people may come here in future in search of a secure method, that's the reason why I added the secure method too. It's a little more work to implement, but some people may need it.
    – Fayaz
    Feb 13, 2017 at 15:07
0

As far as I know, .htaccess (or apache config) is prioritized and you cant override it easily. There are two solutions:

1) Just use the simple-browser.php file (modify path of protected directory as you need)

2) Dont use apache config, instead, use .htaccess inside that directory, here is how:

create .htaccess in that folder:

<Directory "./">
Order allow,deny
Require all granted

#Our Network
Allow from ip-address/22
........
#DONT_REMOVE_ME_START
#DONT_REMOVE_ME_END
</Directory>

At first, put below content in your functions.php and after that, send users to this link: yoursite.com/?check_permission:

define('path_to_target_htaccess',   ABSPATH.'path/to/protected/folder/.htaccess');

add_action('init', 'start_validation');
function start_validation(){
    if (isset($_GET['check_permission'])){
        if(is_user_logged_in()){

            $new_content=  str_replace(
                '#DONT_REMOVE_ME_START',  
                '#DONT_REMOVE_ME_START'."\r\n".'Allow from '.$_SERVER['REMOTE_ADDR'], 
                file_get_contents(path_to_target_htaccess)
            );
            file_put_contents(path_to_target_htaccess,  $new_content);
            header("location:  /path/to/folder"); exit;
        }
    }
}

p.s. once in a week or day, run a function that will delete all lines between #DONT_REMOVE_ME_START and #DONT_REMOVE_ME_END

0

I haven't been in this situation and know more about WP than Apache, but here is roughly how I would approach it.

  1. Use mod_setenvif to set an environment variable based on the requested url containing a certain code, say 'wp-login-!637#6'.
  2. Allow access if the environment variable is correct. This will fail.
  3. Redirect to a special page in the WP install, passing the original url as a query variable, like this: Redirect "foo.html" "wp/specialpage?foo.html".
  4. Now you're in WP and can test whether the current user is logged in. If not, allow login or display 'forbidden'. If logged in, redirect to the original url, appending the code 'wp-login-!637#6'
  5. Now you're back at step one, but this time the environment variable will be set correct and you get access to the file

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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