Logins with mod_rewrite + Cookies + JavaScript
I recently had an odd situation come up, though it’s not incredibly hard to imagine. I needed to create a staging web server, identical to production, but it should live on a different hostname (eg, staging.sherman.bz, instead of www.sherman.bz). The content should not accessible to the public, nor to search engines. This isn’t really so much a privacy or security issue as it is a convenience and customer service issue. If search engines somehow picked up on our staging site (and they would, given it’s full of SEO triggering info, google analytics scripts and the like) then our search information could become poisoned with the staging URL. Also bad would be if a customer found the staging site and posted information to the wrong place. I’ve actually seen both of these happen in the past when measures were not in place to prevent it.
My typical answer to this problem has been to add trusty HTTP Basic Authentication, frequently done via an .htaccess file or similar method which is described in detail in Apache’s HTTPd documentation. So, at first I implemented something like this:
<LocationMatch .*>
AuthType Basic
AuthName "Staging Login"
AuthUserFile /var/www/staging-auth
Require valid-user
</LocationMatch>
After testing I realized that some of the applications installed were aware of the HTTP Basic Auth mechanism; in fact they honored it as an alternative to their default authentication method. So, by requiring this login method, I was breaking my staging system; website and applications on the site could not be tested in a manner similar to production.
So, then came the idea to use mod_rewrite to check for a special, custom cookie; if not present mod_rewrite would redirect the user to a custom HTML page that was totally self contained with Javascript capable of setting the cookie that mod_rewrite was checking for. The plan didn’t require intense security, but to keep out the average user and search spider, I decided to embed Javascript versions of the MD5 hashing algorithm and store a hash of the one allowed username and password. Obviously, storing even the hash of the username and password in the HTML file makes this susceptible to attack and is not secure at all, but I repeat, this is just enough to stop average users and search spiders. If it was desired the idea could be extended to authenticate against a user service or database and become much more secure, but that isn’t my goal here. My goal is to have minimal impact to my staging system. So, with this solution I end up with only two places I need to change things: 1) I have to add an HTML file to the filesystem (/var/www/html/staging-login.html) and 2) add the following to a valid location in my httpd.conf, probably near the top of my VirtualHost definition, etc:
<LocationMatch .*>
RewriteEngine On
RewriteCond %{HTTP_COOKIE} !staging-login
RewriteCond %{REQUEST_FILENAME} !/staging-login.html
RewriteRule ^(.*)$ /staging-login.html? [R,L]
</LocationMatch>
Note that the <LocationMatch .*> is just a way to make this apply to any URI on your site, the rewrite rules can be place in any valid location per the Apache HTTPd mod_rewrite documentation.
The end result is that any user who doesn’t have the “staging-login” cookie will be redirected to the /staging-login.html file and asked to login. The login credentials in this file are user “admin” password “staging” but they can be changed; some ideas on how to change the stored hash are included in the file comments. Once the user has typed the valid username and password, Javascript sets the valid cookie and redirects them back to the base of the site.
So far this seems to be a really nice solution because it has NO impact on any other applications or code running on the site. Those applications don’t know or care about this little cookie sitting there; they certainly aren’t effected adversely by it. And of course, the main goal is accomplished, only “valid” users can see your staging site.
Hope you enjoy my solution to this fun and interesting problem!
Download Sample: staging-login.html (right-click and select “Save As”)