We’ll
start by observing that web servers aren’t the only things that
can issue Authorization:
headers. Scripts can do
that too. Example 12.1 is a simple Perl script that
challenges for a name and password, just as an authenticating web
server does.
Example 12-1. Scripting the Name/Password Challenge
use MIME::Base64; if ( ! defined $ENV{HTTP_AUTHORIZATION} ) # if no Authorization: header { print "HTTP/1.0 401 Authentication "; # issue authorization challenge print "WWW-Authenticate: Basic realm="subscribers" "; return; } print "HTTP/1.0 200 Ok "; # needed for ISAPI Perl or mod_perl print "Content-type: text/html "; # the standard header $ENV{HTTP_AUTHORIZATION} =~ m/Basic (.+)/i; # get MIME-encoded credentials print "Hello " . decode_base64($1); # print "Hello Aladdin:open sesame"
We’ve introduced another CPAN module here.
MIME::Base64 converts back and forth between plain
text and the Base64 encoding used by the HTTP basic authentication
protocol. If you put this code in a file called
auth.pl, put that file into the
/cgi-bin
directory of an Apache web server, and ask your
browser to fetch /cgi-bin/auth.pl, you’ll
provoke an authentication dialog. Type in the credentials
Aladdin and open sesame
and
you’ll get the reponse Hello Aladdin:open sesame
.
If that doesn’t work, define the symbol
SECURITY_HOLE_PASS_AUTHORIZATION
and rebuild
Apache. What? Open a security hole? Well, here’s what the
Apache source code says about allowing scripts to see the
Authorization:
header:
/* * You really don't want to disable this check, since it leaves you * wide open to CGIs stealing passwords and people viewing them * in the environment with "ps -e". But, if you must... */
On an Apache server that hosts multiple CGI-capable sites, it’s
probably a good idea to hold Authorization:
headers close to the vest. Apache wisely defaults to this behavior.
But if you run your own Apache server on a dedicated machine that you
control, it’s reasonable to pass the
Authorization:
header through to your scripts.
IIS, by the way, always does so.
Here’s how this protocol works. The initial request sends no
Authorization:
header, so the script responds with
a challenge, then exits. Why does it exit? The code following the
credentials check is protected. When the browser retries the request,
the script sees an Authorization:
header and the
protected code runs.
Clearly we need to do more than just check for the presence of an
Authorization:
header. We’ll want to look up
the credentials somewhere and decide whether to authenticate the
user. Having done so, the protected code will be in a position to do
more. It could, for example, look up the value of the
company
field in a requested docbase record and
compare that with the list of companies subscribed to by the
authenticated user. But before we build that piece, let’s look
at why this script won’t work the same way in IIS and how to
fix that.