Restrict acess Basic Auth to all but one file+query string - apache

I need to prevent access to all files in a dir but one specific file, with a specific query string. I've been trying this so far, but I REQUEST_URI doesn't have the QUERY STRING
SetEnvIf Request_URI ^index.php?myquery$ noauth=1
AuthType Basic
AuthUserFile /home/user/.htpasswd
AuthName "Pass"
Order Deny,Allow
Satisfy any
Deny from all
Require valid-user
Allow from env=noauth
Again, the user should only be able to access index.php?myquery. Not index.php or anything.php?myquery

SetEnvIf cannot access the query string in Apache 2.2. In 2.4, you could access the query string in a 2nd SetEnvIf or just use <if>
Here's a simple example in mod_rewrite though that works in 2.2 vhost context:
RewriteEngine ON
RewriteCond %{REQUEST_URI} =/index.php
RewriteCond %{QUERY_STRING} =myquery
RewriteRule .* - [E=noauth:1]
Note: doesn't work in htaccess/per-dir context

Don't think you can do what you want here since you can't use mod_rewrite to alter the request (auth module happens before rewrite module) and you can't match against the query string in the request URI. You may need to do something with some redirect trickery, maybe something like:
make a symlink of index.php called index2.php (so that they are the same file)
make the SetEnvIf call match simply ^/index.php
Within the index.php script (probably near the very top), have it look for whether or not there's a myquery query string, and if not, redirect the request to /index2.php
When the browser is told to go to /index2.php instead, it gets asked for authentication
If the browser enters the correct auth, index.php gets executed (because of step #1).

If you are using Apache 2.4, SetEnvIf and mod_rewrite workarounds are no longer necessary since the Require directive is able to interpret expressions directly:
Apache 2.4 treats Require directives that are not grouped by as if they were in a , which behaves as an "or" statement.
Allow access to /callbacks/foo
Require expr %{REQUEST_URI} =~ m#^/callbacks/.*#
Require valid-user
Allow access to /callbacks/foo?secret_var=42 :
Require expr %{QUERY_STRING} = 'secret_var=42'
Require valid-user
This example would allow access to /callbacks/foo?secret_var=42 but require a username and password for /callbacks/foo :
<RequireAny>
<RequireAll>
# I'm using the alternate matching form here so I don't have
# to escape the /'s in the URL.
Require expr %{REQUEST_URI} =~ m#^/callbacks/.*#
Require expr %{QUERY_STRING} = 'secret_var=42'
</RequireAll>
Require valid-user
</RequireAny>
More examples.

Related

Authentication with apache: pass group as header to application server

I would like to use Apache web server as a reverse proxy in front of an application server to handle authentication.
The idea is that after authentication Apache will pass on the user and group(s) to the app server in request headers.
How can I capture the group(s) of the authenticated user in an environment variable so that I can use it for setting request headers?
I've managed to write the user to a header like so:
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1,NS]
RequestHeader set X-Forwarded-User %{RU}e
I'm assuming it would be similar for groups, but I can't find what variable I should use in RewriteCond. (Or is there another way to do it?)
RewriteCond %{???} (.+) # <--- what variable should I use here
RewriteRule . - [E=RG:%1,NS]
RequestHeader set X-Forwarded-User-Groups %{RG}e
A more complete example of the configuration I'm trying to use:
<VirtualHost *:80>
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
<Location />
AuthType Basic
AuthBasicProvider file
AuthName "Restricted Content"
AuthUserFile "/path/to/userfile"
AuthGroupFile "/path/to/groupfile"
Require group users
Require group admins
RewriteEngine On
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1,NS]
RequestHeader set X-Forwarded-User %{RU}e
RewriteCond %{???} (.+)
RewriteRule . - [E=RG:%1,NS]
RequestHeader set X-Forwarded-User-Groups %{RG}e
RequestHeader unset Authorization
</Location>
</VirtualHost>
I just had the same issue, and after an extensive search and looking at the mod_authz_groupfile.c source it just doesn't seem possible with just configuration.
The group is not exposed as a variable, and there doesn't seem to be a way to use the require group statement in an expression. You could probably get the group into a variable using the RewriteMap directive to read the AuthGroupsFile again with a custom external command (the default commands like txt aren't sufficient), but that is way complicated and probably slow.
Note that using the file function to read the AuthGroupsFile within an <If> expression will not work, as the expression is evaluated before authentication and thus the value of the REMOTE_USER variable will not yet be available.

Apache ACL based on environment variable value

I have a vendor Apache module (PingFederate) that sets environment variables based on a token it receives. I would like to control access to directories based on the value of an environment variable.
For example the module sets variables like this:
[PF_AUTH_SUBJECT] => aaron
[PF_AUTH_GROUPS] => CN=Application.E18.Users,OU=Internal,DC=local,CN=Application.E17.Users,OU=Internal,DC=local
I want to secure a directory so that only users in group CN=Application.E18... can access it. My location direction conf looks like this:
<Location /example_app>
SetEnvIf %{PF_AUTH_GROUPS} ^.*CN=Application.E18.Users.*$ ALLOWED
AuthName "ACL PingFederate restricted"
AuthType PFApacheAgent
Order Deny,Allow
Deny from all
Allow from all
</Location>
This doesn't seem to work. I've tried:
SetEnvIf %{PF_AUTH_GROUPS} ^.*CN=Application.E18.Users.*$ ALLOWED
SetEnvIf %{PF_AUTH_GROUPS} ^.*Application.*$ ALLOWED
SetEnvIf %{PF_AUTH_GROUPS} ^.*A.*$ ALLOWED
The only thing that works is:
SetEnvIf %{PF_AUTH_GROUPS} ^.*$ ALLOWED
That obviously won't work.
https://issues.apache.org/bugzilla/show_bug.cgi?id=25725 somewhat intimates that SetEnvIf won't test environment variables but the docs at http://httpd.apache.org/docs/2.2/mod/mod_setenvif.html mentions "an environment variable in the list of those associated with the request", which this should be.
I've also tried mod_rewrite using this:
RewriteEngine On
<Location /example_app>
RewriteCond %{PF_AUTH_GROUPS} ^.*Application.E18.Users.*$
RewriteRule - [E=ALLOWED:1]
AuthName "ACL PingFederate restricted"
AuthType PFApacheAgent
Order Deny,Allow
Deny from all
Allow from all
</Location>
In all of these instances the ALLOWED environment variable is not set.
You'll need to make mod_rewrite execute 2 times to be able to leverage the headers produced by mod_pf, since the latter executes after the former:
RewriteEngine On
RewriteCond %{ENV:REDIRECT_PASS} !1
RewriteRule .* $1 [L,E=PASS:1]
RewriteCond %{HTTP:PF_AUTH_GROUPS} !^.*ECN.*$
RewriteRule .* $1 [L,R=401]
This is also documented here: https://ping.force.com/Support/PingIdentityArticle?id=kA340000000Gs7bCAC

use basic auth based on query_string

I'm trying to protect urls containing a specific string:
RewriteCond %{QUERY_STRING} foo
RewriteRule - [E=NEED_AUTH:1]
Options -Indexes
AuthType Basic
AuthUserFile /etc/apache2/.htpasswd
Order allow,deny
allow from all
satisfy any
deny from env=NEED_AUTH
Require valid-user
I suppose this should bring up the authentication dialog when loading
index.php?format=foo
but it doesn't work. I tried several other RewriteConds like for example
RewriteCond %{QUERY_STRING} format=foo
RewriteCond %{QUERY_STRING} ^format=foo$
with no luck. Using
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 3
doesn't log anything.
Any suggestions? :)
To add to Jon's answer, I had a similar issue as the OP. But I found that allow from env=!NEED_AUTH was not working so I had to reverse it in the Rewrite Condition RewriteCond %{QUERY_STRING} !format=foo and then reverse the allow allow from env=NEED_AUTH. So, here is the snippet that works:
RewriteCond %{QUERY_STRING} !foo=bar
RewriteRule ^ - [E=NO_AUTH:1]
Order deny,allow
Deny from all
AuthType basic
AuthName "Auth Needed"
AuthUserFile "/etc/httpd/conf.d/.htpasswd"
Require valid-user
Allow from env=NO_AUTH
Satisfy Any
Make sure you've turned on the rewrite engine using RewriteEngine On. Also, there's an error in your RewriteRule:
RewriteRule - [E=NEED_AUTH:1]
Needs to have 2 parameters before the flags:
RewriteRule ^ - [E=NEED_AUTH:1]
Aside from that, it doesn't look like you've setup authentication correctly. You'll need a AuthName to define the realm, apache won't do authentication without it. And I don't think you can force authentication with a allow from all, you might need to do this the other way around, by denying all and allow from env=!NEED_AUTH
As described here (Apache permissions based on querystring), I cannot get the combination of "RewriteRule" and "Allow from env" working in Apache 2.4.6.

Can an htpasswd apply to all URLs except one?

I have an htaccess setup for my domain, but I want one specific URL to not have htpasswd protection. That URL is a rewritten URL and doesn't have an actual directory associated with it.
Is there a way to use the root htaccess to htpasswd protect the entire site except for that one specific URL?
Edit (my code, including Jon Lin's suggestion, which isn't working for me):
RewriteCond %{SERVER_NAME} =subdomain.example.com
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
SetEnvIf Request_URI "^/messenger/parse" NOPASSWD=true
AuthUserFile /xxx/xxx/xxx/xxxxxx/subdomain.example.com/.htpasswd
AuthName "Staging Server"
AuthType Basic
Order Deny,Allow
Satisfy any
Deny from all
Require valid-user
Allow from env=NOPASSWD
You can setup something like SetEnvIf to set an environment variable for a given Request URI. Then in the auth definition, you can use the Satisfy Any and Allow from env directives to tell apache that access can be granted either by authentication or if the environment variable exists (which is only set for a specific URI). Example:
# set the NOPASSWD variable if the request is for a specific URI
SetEnvIf Request_URI "^/specific/uri/nopasswd/$" NOPASSWD=true
# Auth directives
AuthUserFile /var/www/htpasswd
AuthName "Password Protected"
AuthType Basic
Order Deny,Allow
# Any requirment satisfies
Satisfy any
# Deny all requests
Deny from all
# except if user is authenticated
Require valid-user
# or if NOPASSWD is set
Allow from env=NOPASSWD

htaccess: Conditional Authentication

How can i configure this in my apache/htaccess configuration:
I want HTTP-Authentication for all files with one exception.
The files click.php and js/clickheat.js may be accessed from 1.2.3.4 without any authentication.
I tried FilesMatch, but i can't invert it. So i can't put require valid-user in there. I thought using SetEnv, but how do i check for it later?
<FilesMatch "(click\.php|clickheat\.js)">
# what here?
</FilesMatch>
My other idea was to use mod_rewrite. Well, i can allow access to the two files from the given host and deny it from anywhere else. But how do i chain it with HTTP-Authentication?
# allows access to two files from the given IP
RewriteCond %{REMOTE_ADDR} 1\.2\.3\.4
RewriteCond %{REQUEST_URI} ^/click.php [OR]
RewriteCond %{REQUEST_URI} ^/js/clickheat\.js
RewriteRule (.*) - [L]
# denies everything else
RewriteRule (.*) - [F,L]
So my favourite solution would be enabling HTTP-Auth via RewriteCond/RewriteRule.
Background (or why i want to do this): I'm trying to secure a clickheat (http://www.labsmedia.com/clickheat/index.html) installation. And 1.2.3.4 is the remote running mod_proxy and redirecting access to these to files to our clickheat host.
Ok, i found a solution:
Order Deny,Allow
Deny from All
Satisfy Any
AuthType Basic
AuthName "clickheat"
AuthUserFile /var/www/clickheat/.htpasswd
Require valid-user
<FilesMatch "(click\.php|clickheat\.js)">
Deny from All
Allow from 1.2.3.4
</FilesMatch>
The key is the Satisfy Any which allows either IP- or Auth-based access.