Search code examples
phpapache.htaccessmod-rewritedrupal-7

.htaccess basic auth combined with ip restriction


I would like to block a path from my site using the .htaccess configuration. The idea is that only a specific set of IP's can access that specific path from the URL after they authenticated using basic auth.

Note: It's a path, not a page or directory. We are trying to shield off a web-service so there will be only post calls to the URL's.

I would like the url example.com/rest to be blocked and everything behind that url based on IP. So example.com/rest/foo and example.com/rest/foo/bar should be blocked.

All other paths from the application should remain functional and without basic auth.

The IP blocking part has been resolved in a previous question I asked.

The basic configuration (the blocking part, there is more in the .htaccess but is not relevant to this question.) you can find below.

SetEnvIf Request_URI "/rest(/.*)?$" rest_uri
# Check on what subdomain we are.
SetEnvIf Host ^local\. None_Prod_Env

# Static
SetEnvIf AH_CLIENT_IP ^123\.123\.123\.123$ Allow_Host
# Range
SetEnvIf AH_CLIENT_IP ^123\.123\.123\. Allow_Host

Order deny,allow
Deny from all
Allow from env=!rest_uri
Allow from env=Allow_Host
Allow from env=None_Prod_Env

So the configuration above blocks all access to /rest/* but not to non rest paths, it allows a user coming from IP X (Allow_Host variable) and we allow none production environments in this case local.

I tried to extend this functionality with basic auth like so:

SetEnvIf Request_URI "/rest(/.*)?$" rest_uri
SetEnvIfNoCase Request_URI "/rest(/.*)?$" require_auth=true

# ... Allow Host stuff and none prod stuff ...

Order deny,allow
Deny from all
Allow from env=!rest_uri
Allow from env=Allow_Host
Allow from env=None_Prod_Env

AuthName "Password Protected"
AuthType Basic
AuthBasicProvider file
AuthUserFile /var/www/html/.htpasswd
Require valid-user

However this resulted in a basic auth on all pages and not only for the /rest/* url. I played a lot with it but couldn't figure it out. Changing SetEnvIfNoCase to SetEnvIf also didn't help.

Note: Our server is running apache 2.2.22.


Solution

  • You can solve this complex problem using a combination of few Apache directives i.e. mod_dir, mod_setenv and mod_auth_basic:

    SetEnvIf Request_URI ^/rest(/.*)?$ rest_uri
    # Check on what subdomain we are.
    SetEnvIf Host ^local None_Prod_Env
    
    # Static
    SetEnvIf AH_CLIENT_IP ^123\.123\.123\.123$ Allow_Host
    # Range
    SetEnvIf AH_CLIENT_IP ^192\.168\. Allow_Host
    
    RewriteEngine On
    
    # block if request is /rest/* and IP is not whitelisted and not localhost
    RewriteCond %{ENV:rest_uri} =1
    RewriteCond %{ENV:None_Prod_Env} !=1
    RewriteCond %{ENV:Allow_Host} !=1
    RewriteRule ^ - [F]
    
    # ask auth for /rest/* && NOT localhost && whitelist IP
    AuthType Basic
    AuthName "Password Protected"
    AuthUserFile /var/www/html/.htpasswd
    Require valid-user
    
    Order deny,allow
    Deny from all
    Allow from env=!rest_uri
    Allow from env=!Allow_Host
    Allow from env=None_Prod_Env
    Satisfy any