.htaccess: how to restrict access to a single file by IP? - apache

I've look all over, but keeps running into same info that talks about directory level IP restriction, which usually looks something like this:
Order Deny,Allow
Deny from all
Allow from 123.123.123.123
Is it possible to have same type of access restriction tied to a page/document?

This will allow either someone from IP 127.0.0.1 or logged as a valid user. Stick it either in your config or .htaccess file.
<Files learn.php>
Satisfy any
Order deny,allow
Deny from all
Allow from 127.0.0.1
AuthType Basic
AuthName "private"
AuthUserFile /var/www/phpexperts.pro/.htpasswd
AuthGroupFile /dev/null
Require valid-user
</Files>
IP Alone:
<Files learn.php>
Order deny,allow
Deny from all
Allow from 127.0.0.1
</Files>
That definitely answers your question.

I think the directive needs to be:
Order deny,allow
for the answer above to work (at least for the IP Alone solution).

Mod-rewrite based solution :
RewriteEngine on
RewriteCond %{REMOTE_ADDR} !^Y\.O\.U\.R\.IP$
RewriteRule ^file\.php$ - [F,L]
The rewriteRule above will deny all requests to file.php if client ip does not match the ip address in the RewriteCond's pattern

For a more up to date Apache 2.4 example:
<Files file.html>
Require ip 123.123.123.123
</Files>
Here are the more in depth docs for additional options and examples: https://httpd.apache.org/docs/2.4/howto/access.html and the docs for the <Files> directive: https://httpd.apache.org/docs/2.4/mod/core.html#files
Note that <Files> can be nested inside <Directory> sections to restrict the portion of the filesystem they apply to.

Related

Only allow certain IP's to access website, EXCEPT a specific URL (open to any IP)

I'm aware of the below in .htaccess which only allows a certain IP to access a website:
Order deny,allow
Deny from all
Allow from xxx.xxx.xxx.xxx
But I want to extend this to allow any IP to access a specific URL (i.e. www.mywebsite.com/any).
So my IP can access the whole website, but my friend's IP can only access a specific URL. It doesn't need forwarding to.
How do I go about doing this?
For anyone in a similar situation, here's the code I used which works:
<Files "">
Order allow,deny
allow from 192.168.0.1
</Files>
<Files "any">
Order allow,deny
allow from all
</Files>
You can use mod-rewrite instead of Allow/deny directives :
RewriteEngine on
#redirect client to /any if not my ip address
RewriteCond %{REMOTE_ADDR} !^YourIP$
RewriteRule !any /any [L,R]
This will redirect all other ip address except yourIP to /any .
"Location /" is the root of your url (mysite.com), only the specific ip (192.168.0.1o for example) has access and Location /all is part of the site that everyone das access (mysite.com/all).
<Location />
Order Allow,deny
Allow from myIP #Allow from 192.168.0.10
</Location>
<Location /all>
Order Allow,deny
Allow from all
</Location>
Easy peasy If you read the doc's

Apache permissions based on querystring

I have an apache server where authentication is required, but there are some calls that need to be allowed for all.
On off these calls is based on a query string for example:
/foo/api.php?Token=123&Task=DoStuff&Result=json
I taught that with a LocationMatch that this would have workd so i worked out this configuration:
<LocationMatch ^/foo/api.php\?.*(Task=DoStuff).*>
Order Allow,Deny
Allow from All
</LocationMatch>
But this doesn't let me pass the authentication (meaning i get a 401).
If I just filter ^/foo/api.php I get passed the authentication, but this isn't strict enough.
Anyone has any idea how to configure this to check the Task parameter in the querystring?
For authentication we are using kerberos, this is forced on the whole site
This is our conf for kerb
LoadModule auth_kerb_module modules/mod_auth_kerb.so
<Directory /var/www/html>
Options FollowSymLinks
AllowOverride All
AuthType Kerberos
Require valid-user
AuthName "Kerberos Login"
KrbMethodNegotiate on
KrbMethodK5Passwd on
KrbAuthRealms FOO.LOCAL
KrbServiceName HTTP/server.foo.local#foo.LOCAL
Krb5KeyTab /etc/httpd/conf/http.keytab
Satisfy Any
Order deny,allow
Deny from all
Allow from 192.168.72.90
Allow from 192.168.72.91
Allow from 192.168.72.94
Allow from 192.168.72.95
Allow from 127.0.0.1
</Directory>
As you can read here:
The <Location>, <LocationMatch>, <Directory> and <DirectoryMatch>
Apache directives allow us to apply authentication/authorization to
specific patterns of resources with a high degree of specificity, but
do not give us that control down to the query-string level.
Therefore, you have to use mod_rewrite to achieve your goal.
For example:
RewriteEngine on
RewriteCond %{QUERY_STRING} Task=DoStuff
RewriteRule ^/foo/api.php - [E=no_auth_required:1]
<LocationMatch ^/foo/api.php>
Order allow,deny
Allow from env=no_auth_required
AuthType Basic
AuthName "Login Required"
AuthUserFile /var/www/foo/.htpasswd
require valid-user
Satisfy Any
</LocationMatch>
UPDATE
You've stated that:
If I just filter ^/foo/api.php I get passed the authentication, but
this isn't strict enough.
Then, try adding the following rows to your configuration:
RewriteEngine on
RewriteCond %{QUERY_STRING} Task=DoStuff
RewriteRule ^/foo/api.php - [E=no_auth_required:1]
<LocationMatch ^/foo/api.php>
Order allow,deny
Allow from env=no_auth_required
</LocationMatch>
In Apache 2.4 there's an easier way to do this without the rewrite.
You can use LocationMatch to match upto the QUERY_STRING, and then use an If block to match the contents of QUERY_STRING. i.e., something like this:
<LocationMatch "^/foo/api.php">
<If "%{QUERY_STRING} =~ /.*Task=DoStuff.*/" >
Require all granted
</If>
</LocationMatch>
we can't use <If %{QUERY_STRING} /a=b/>, is not supported by < apache 2.4
RewriteEngine On
RewriteCond %{QUERY_STRING} test=ok
RewriteRule ^/ - [E=checkParamTest:1]
<LocationMatch "^/">
Order allow,deny
Allow from env=checkParamTest
Satisfy any
</LocationMatch>
<Location />
AuthType openid-connect
Require valid-user
...
</Location>

How to make Basic Auth exclude a rewritten URL

I have a Basic Authentication setup on a development server. It is setup inside my httpd.conf file for the VirtualHost of the website. I've had to set up it to exclude certain directories, which has caused no problems and all works fine.
The issue has been with excluding a URL that has been through my mod_rewrite rules in the .htaccess file. My set up is that I have all URLs going through my index.php file and from there the relevant code is found and ran. I tried adding the URL that I wanted to exclude (/businesses/upload_logo) like I did the others but it still requires authentication. This is what I currently have:
...
<Location />
SetEnvIf Request_URI "/businesses/upload_logo" noauth=1
SetEnvIf Request_URI "/api/.*" noauth=1
AuthType Basic
AuthName "Private"
AuthUserFile ****
Require valid-user
Order deny,allow
Satisfy any
Deny from all
Allow from env=noauth
</Location>
....
I have found questions that are similar to mine here & here but the answers only give me what I'm already trying.
I have thought of possible other solutions as well, but these will be last resort things. I want to see if it's possible the way I'm currently doing it:
Set up the basic auth inside my php code instead
Too much hassle at the moment
Put the authentication in my .htaccess file instead
Didn't want to do this just yet as I only want the authentication to happen on one of 3 servers. I'm aware that I could use some more SetEnvIf HOST ... but I'm looking to see if it can be fixed this way or not first.
The mod_rewrite rule:
...
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php [L,QSA]
Try adding
Allow from env=REDIRECT_noauth
For me something like this works like a charm:
<location />
SetEnvIf Request_URI "/businesses/upload_logo" REDIRECT_noauth=1
AuthType Basic
AuthName "Restricted Files"
AuthUserFile /etc/httpd/passwords/passwords
Order Deny,Allow
Satisfy any
Deny from all
Allow from env=REDIRECT_noauth
Require user yournickname
</location>
based on what you have given it should work, unless there is a conflicting directive somewhere else in your configuration.
i have made a similar working setup , just i have used filesystem path instead of URI
i am adding it here, hoping you may find it useful
<VirtualHost *:8989 >
<IfModule mod_auth_basic.c>
<Directory /var/www/html/vella-8989>
# the auth block
AuthType Basic
AuthName "Please login."
AuthUserFile /var/www/html/vella-8989/.htpasswd
require valid-user
Order Deny,Allow
Satisfy any
Deny from all
Require valid-user
Allow from env=noauth
</Directory>
</IfModule>
# set an environtment variable "noauth" if the request has "/callbacks/"
SetEnvIf Request_URI "/callbacks/" noauth=1
ServerName vella.com
ServerSignature off
</VirtualHost>

.htaccess basic auth by virtual host?

I was wondering if it was possible to setup a conditional http basic auth requirement based on the virtual host URL in an .htaccess file.
For example what I want to do is have mysite.com and test.mysite.com run off the same code base in the same directory but password protect test.mysite.com. It would be setup this way so that I wouldn't need to branch my code since my app code can see which vhost/url it's being served from and pick the database to serve content from.
You can sort of kludge this by using mod_setenvif along with the mod_auth modules. Use the SetEnvIfNoCase directive to set which host is password protected. You'll need a couple of extra directives to satisfy access:
# Check for the hostname here
SetEnvIfNoCase HOST ^test\.mysite\.com\.?(:80)?$ PROTECTED_HOST
Then inside the Directory block (or just out in the open) you have your auth stuff setup, something like this:
AuthUserFile /var/www/test.mysite.com/htpasswd
AuthType Basic
AuthName "Password Protected"
Now for the require/satisfy stuff:
Order Deny,Allow
Satisfy any
Deny from all
Require valid-user
Allow from env=!PROTECTED_HOST
This will make it so any host that doesn't match ^test\.mysite\.com\.?(:80)?$ will have access without need for auth (Allow from env=!PROTECTED_HOST) but otherwise, we need a valid user (Require valid-user). The Satisfy any ensures that we just need one of the 2, either the Allow or Require.
I had problems implementing Jon's solution:
Although I am quite familiar with Apache conf and regular expressions, the authentication always fired. From a quick analyzes it looked like the Allow from env=!PROTECTED_HOST line did not kick in.
But I found another solution that actually looks safer to me:
I created two virtual hosts for the two domains pointing to the same document root (which is fully allowed by the way). In one of the vhosts I added the directives for basic auth (directly into the vhost directive block).
Works like a charm. And I have a better feeling that this is really safe - no risk to overlook any details in the regex pattern that would open up the gates for intruders.
<VirtualHost *:80>
ServerName www.mysite.com
DocumentRoot "/path/to/common/doc/root"
<Directory "/path/to/common/doc/root">
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName protected.mysite.com
DocumentRoot "/path/to/common/doc/root"
<Directory "/path/to/common/doc/root">
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
AuthUserFile /path/to/htpasswd
AuthName "Password please"
AuthType Basic
Require valid-user
</Directory>
</VirtualHost>
Here's a solution similar to what Jon Lin proposed, but using RewriteCond to check the host name:
RewriteEngine On
RewriteCond %{HTTP_HOST} =protected.hostname.com
RewriteRule ^.*$ - [E=DENY:1]
AuthUserFile /path/to/htpasswd
AuthName "Password please"
AuthType Basic
Order Deny,Allow
Satisfy any
Deny from all
Require valid-user
Allow from env=!DENY

Use HTTP Auth only if accessing a specific domain

I've got several sites: example.com, example1.com, and example2.com. All of them point to my server's /public_html folder, which is my Apache root folder.
What do I need to add to my .htaccess file to use http authentication only if the user is coming from example2.com? example.com and example1.com should NOT use authentication.
I know I need something like
AuthType Basic
AuthName "Password Required"
AuthUserFile "/path/to/.htpasswd"
Require valid-user
But I only want to require a password if the user is visiting example2.com.
Edit
Using an approach suggested in an answer, I have the following in my .htaccess file:
SetEnvIfNoCase Host ^(.*)$ testauth
<IfDefine testauth>
RewriteRule ^(.*)$ index2.php?q=$1 [L,QSA]
</IfDefine>
I know that the mod_setenvif.c module is enabled (I verified with an <IfModule> block), but it would appear that "testauth" is never getting defined, because my test to verify (redirecting to index2.php) is not executing (whereas it was getting executed in my <IfModule> block). Any ideas why?
How about something along the lines of this in the htaccess file in the document root:
# set the "require_auth" var if Host ends with "example2.com"
SetEnvIfNoCase Host example2\.com$ require_auth=true
# Auth stuff
AuthUserFile /var/www/htpasswd
AuthName "Password Protected"
AuthType Basic
# Setup a deny/allow
Order Deny,Allow
# Deny from everyone
Deny from all
# except if either of these are satisfied
Satisfy any
# 1. a valid authenticated user
Require valid-user
# or 2. the "require_auth" var is NOT set
Allow from env=!require_auth
This will make it so authentication is not required unless the host ends with example2.com (e.g. www.example2.com, dev.example2.com, etc). The expression can be tweaked if needed. Any other host will cause the require_auth var not to get set so authentication is not required. If this needs to be the other way around, the last line could be changed to: Allow from env=require_auth, removing the !.
Apache 2.4 offers a semantic alternative with the If directive:
<If "req('Host') == 'example2.com'">
AuthUserFile /path/to/htpasswd
AuthType Basic
AuthName "Password Protected"
Require valid-user
</If>
<Else>
Require all granted
</Else>
Here is one recommendation:
Create a file called common.conf and save in an accessible location
In this file place the Apache configuration common to all sites (hosts).
The remove the current single VirtualHost entry an replace with VirtualHost entries as follows:
# These are the password protected hosts
<VirtualHost *:80>
ServerName example.com
ServerAlias example1.com
Include /path-to-common-configuration/common.conf
AuthType Basic
AuthName "Password Required"
AuthUserFile "/path/to/.htpasswd"
Require valid-user
</VirtualHost>
# These are hosts not requiring authentication
<VirtualHost *:80>
ServerName example2.com
ServerAlias example3.com
Include /path-to-common-configuration/common.conf
</VirtualHost>
I wonder if DanH would be helped by an approach that allows access per IP address?
Something like
SetEnvIf Remote_Addr 1\.2\.3\.4 AllowMeIn
SetEnvIfNoCase Host this\.host\.is\.ok\.com AllowMeIn
SetEnvIfNoCase Host this\.host\.is\.also\.ok\.com AllowMeIn
and then in your Drupal "container"
Order Allow,Deny
Allow from env=AllowMeIn
should do the trick.
Any host that is "live" should be configured to "AllowMeIn", or else you have to come from a known IP address (ie you and other developers).
You shouldn't be putting per-vhost configuration into .htaccess. Instead, put the config block in the VirtualHost block in the proper config file in /etc/apache/sites-enabled/*.