Monday, July 19, 2010

Query-String-Based Apache Authentication

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.  Today we learn how to use mod_rewrite, environment variables, and the Satisfy directive to allow us to attain that goal!

Do this:

RewriteEngine on
RewriteCond %{QUERY_STRING} action=secret_admin
RewriteRule ^/app/.*$ - [E=app_auth_required:1]

<directory /root/of/your/app>
      Order deny,allow

      Deny from env=app_auth_required
      AuthType Basic
      AuthName "Login Required"

      AuthUserFile some_htpasswd_file
      require valid_user
      Satisfy any
</directory>


To break it down, the RewriteCond gives us access to that meaty query-string information.  In this case, we are matching when a string contains "action=secret_admin" in conjunction with the URL starting with "/app".  The RewriteRule does nothing to the URL (as noted by the "-"), but it does set an environment variable for the duration of the HTTP request.

The second section you might recognize as your standard Apache authentication block, but with a couple additions.  The "Deny from env=app_auth_required" checks for the existence of the environment variable set (or not set) above.  In the affirmative case (being that the environment variable exists and the request is denied) the "Satisfy Any" directive causes Apache to try the other form of authorization to grant access (the Require directive).

If the URL of the request were to contain a query string of the form "action=something_else", the Deny directive is not applied, and so the access criteria is met immediately.  Further authorization is not required.

I love Apache!

7 comments:

Julian Bogdani said...

Hi,
I'm trying to follow your post, but I'm having some problem. I'm a newby on apache, and I think I' missing something stupid.
I've a folder named test with inside a .htaccess having the following content:
#-------------
RewriteEngine on
RewriteCond %{QUERY_STRING} p=1
RewriteRule ^(.*)$ $1?map=$1 [E=authme:1]
Order allow,deny
Deny from env=authme
AuthType Basic
AuthName "Login Required"
AuthUserFile /usr/lib/cgi-bin/.htpasswd
require valid_user
Satisfy any
#---------
I expect to be asked for authentication if p=1 query-string is set, but authentication is required even if no query string is set.
I can check with php (apache_getenv) that apache variable authme is correctly set, but I cannot figure out why it's not working.

lucidiot said...

Hi Julian,

It looks like I have an error in my post. It isn't the first mistake I've ever made and it won't be the last. :)

The "Order allow,deny" directive should be "Order deny,allow".

Further, you might need to have an "Allow from all" directive in your .htaccess depending on if you have a "Deny" directive that might apply to a parent directory.

I'll correct the posting.

Thanks!

Julian Bogdani said...

Thx for your reply.
I need some more help and hope you can help me. Same code as above:
#-------------
RewriteEngine on
RewriteCond %{QUERY_STRING} p=1
RewriteRule ^(.*)$ $1?map=$1 [E=authme:1]
Order deny,allow
Deny from env=authme
AuthType Basic
AuthName "Login Required"
AuthUserFile /var/www/test/.htpasswd
require valid_user
Satisfy any
#---------

.htaccess is located also in /var/www/test (It's just an example)
It works if I go to:
htp://localhost/test no auth is required
htp://localhost/test/?p=1 auth is required
BUT why on earth this does not apply to single files:
htp://localhost/test/index.php?p=1 auth IS NOT required: why?
Am I missing something important?
(Normal auth, with no variables works fine with and without index.php)
Any help would be appreciated!

Stephen said...

Hi,

I'm also new to apache and I am trying to a build redirect of URL's containing query_strings. So far I have this:
RewriteCond %{QUERY_STRING} D487201D-BBCA-EC84-335B-FE32B988CB29
RewriteRule /rtn2/index.cfm http://www......

The input URL in this case is
http://www.somewebsite.info/rtn2/index.cfm?D487201D-BBCA-EC84-335B-FE32B988CB29

Other redirects have much longer Query_strings and each one goes to a very different static URL. There is no way to work out the target from the input URL.

Any assistance would be greatly appreciated. The example above does not work, original URL displays.

Steve Booker

lucidiot said...

Julian, sorry for the delay in replying. I've been preoccupied with my day job. :)

I see what you're saying. I'm getting the same behavior in the context of the .htaccess file. My guess is that the Satisfy Any may be taking into account the Allow from all that is likely within the Apache configuration. After a couple hours of trying to get this to work, I haven't had any luck either. Sorry.

lucidiot said...

@Stephen, where do you intend to redirect to? Is your criteria for redirecting just the presence of any query_string data in a request for /rtn2/index.cfm? If so, use:

RewriteEngine On
RewriteCond %{QUERY_STRING} ^.+$
RewriteRule /rtn2/index.cfm destination [R]

Julian Bogdani said...

@lucidiot
Thank for your reply. I just asked this question on stackoverflow...
Let's hope someone can help me.

http://stackoverflow.com/questions/5094592/htaccess-apache-mod-rewrite-authentication-strange-behavior