Show Contents Previous Page Next Page Chapter 6 - Authentication and Authorization In this section...
Let's look at authentication handlers now. The authentication handler's job is to determine whether the user is who he or she claims to be, using whatever standards of proof your module chooses to apply. There are many exotic authentication technologies lurking in the wings, including smart cards, digital certificates, one-time passwords, and challenge/response authentication, but at the moment the types of authentication available to modules are limited at the browser side. Most browsers only know about the username and password system used by Basic authentication. You can design any authentication system you like, but it must ultimately rely on the user typing some information into the password dialog box. Fortunately there's a lot you can do within this restriction, as this section will show. A Simple Authentication Handler Show Contents Go to Top Previous Page Next PageExample 6-5 implements Apache::AuthAny, a module that allows users to authenticate with any username and password at all. The purpose of this module is just to show the API for a Basic authentication handler. Example 6-5. A Skeleton Authentication Handler package Apache::AuthAny; # file: Apache/AuthAny.pm use strict; use Apache::Constants qw(:common); sub handler { my $r = shift; my($res, $sent_pw) = $r->get_basic_auth_pw; return $res if $res != OK; my $user = $r->connection->user; unless($user and $sent_pw) { $r->note_basic_auth_failure; $r->log_reason("Both a username and password must be provided", return OK; 1; __END__ The configuration file entry that goes with it might be: <Location /protected> AuthName Test AuthType Basic PerlAuthenHandler Apache::AuthAny require valid-user </Location>
For Basic authentication to work, protected locations must define a realm name with AuthName and specify an AuthType of Basic. In addition, in order to trigger Apache's authentication system, at least one require directive must be present. In this example, we specify a requirement of valid-user, which is usually used to indicate that any registered user is allowed access. Last but not least, the PerlAuthenHandler directive tells By the time the handler is called, Apache will have done most of the work in negotiating the HTTP Basic authentication protocol. It will have alerted the browser that authentication is required to access the page, and the browser will have prompted the user to enter his name and password. The handler needs only to recover these values and validate them. It won't take long to walk through this short module: package Apache::AuthAny; # file: Apache/AuthAny.pm use strict; use Apache::Constants qw(:common); sub handler { my $r = shift; my($res, $sent_pw) = $r->get_basic_auth_pw; Apache::AuthAny starts off by importing the common result code constants. Upon entry its handler() subroutine immediately calls the Apache method get_basic_auth_pw(). This method returns two values: a result code and the password sent by the client. The result code will be one of the following:
The password returned by get_basic_auth_pw() is only valid when the result code is return $res if $res != OK;
If get_basic_auth_pw() returns my $user = $r->connection->user; The values we retrieve contain exactly what the user typed into the name and password fields of the dialog box. If the user has not yet authenticated, or pressed the submit button without filling out the dialog completely, one or both of these fields may be empty. In this case, we have to force the user to (re)authenticate: unless($user and $sent_pw) { $r->note_basic_auth_failure; $r->log_reason("Both a username and password must be provided", $r->filename); return AUTH_REQUIRED; }
To do this, we call the request object's note_basic_auth_failure() The resulting log entry will look something like this: [Sun Jan 11 16:36:31 1998] [error] access to /protected/index.html failed for wallace.telebusiness.co.nz, reason: Both a username and password must be provided
If, on the other hand, both a username and password are present, then the user has authenticated properly. In this case we can return a result code of return OK; } The username will now be available to other handlers and CGI scripts. In particular, the username will be available to any authorization handler further down the handler chain. Other handlers can simply retrieve the username from the connection object just as we did. Notice that the Apache::AuthAny module never actually checks what is inside the username and password. Most authentication modules will compare the username and password to a pair looked up in a database of some sort. However, the Apache::AuthAny module is handy for developing and testing applications that require user authentication before the real authentication module has been implemented. An Anonymous Authentication Handler Show Contents Go to Top Previous Page Next PageNow we'll look at a slightly more sophisticated authentication module, Apache::AuthAnon. This module takes the basics of Apache::AuthAny and adds logic to perform some consistency checks on the username and password. This module implements anonymous authentication according to FTP conventions. The username must be "anonymous" or "anybody," and the password must look like a valid email address. Example 6-6 gives the source code for the module. Here is a typical configuration file entry: <Location /protected> AuthName Anonymous AuthType Basic PerlAuthenHandler Apache::AuthAnon require valid-user PerlSetVar Anonymous anonymous|anybody </Location> Notice that the <Location> section has been changed to make Apache::AuthAnon the PerlAuthenHandler for the /protected subdirectory and that the realm name has been changed to Anonymous. The AuthType and require directives have not changed. Even though we're not performing real username checking, the require directive still needs to be there in order to trigger Apache's authentication handling. A new PerlSetVar directive sets the configuration directive Anonymous to a case-insensitive pattern match to perform on the provided username. In this case, we're accepting either of the usernames anonymous or anybody.
Turning to the code listing, you'll see that we use the same basic outline of Apache::AuthAny. We fetch the provided password by calling the request object's get_basic_auth_pw() method and the username by calling the connection object's user() method. We now perform our consistency checks on the return values. First, we check for the presence of a pattern match string in the Anonymous configuration variable. If not present, we use a hardcoded default of While this example is not much more complicated than Apache::AuthAny and certainly no more secure, it does pretty much everything that a real authentication module will do. A useful enhancement to this module would be to check that the email address provided by the user corresponds to a real Internet host. One way to do this is by making a call to the Perl Net::DNS module to look up the host's IP address and its mail exchanger (an MX record). If neither one nor the other is found, then it is unlikely that the email address is correct. Example 6-6. Anonymous Authentication package Apache::AuthAnon; # file: Apathe/AuthAnon.pm use strict; use Apache::Constants qw(:common); my $email_pat = '[.\w-]+\@\w+\.[.\w]*[^.]'; my $anon_id = "anonymous"; sub handler { my $r = shift; my($res, $sent_pwd) = $r->get_basic_auth_pw; return $res if $res != OK; my $user = lc $r->connection->user; my $reason = ""; my $check_id = $r->dir_config("Anonymous") || $anon_id; $reason = "user did not enter a valid anonymous username " unless $user =~ /^$check_id$/i; $reason .= "user did not enter an email address password " if($reason) { $r->note_basic_auth_failure; $r->log_reason($reason,$r->filename); return AUTH_REQUIRED; } $r->notes(AuthAnonPassword => $sent_pwd); return OK; } 1; __END__Show Contents Go to Top Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |
HIVE: All information for read only. Please respect copyright! |