=head1 Protecting Your Site

=head1 The Importance of Your site's Security

Let's face it, your site or service can easily become a target for
Internet "terrorists". It can be because of something you said, the
success of your site, or for no obvious reason whatever. If your site
security is compromised, all your data can be deleted or important
information can be stolen.  You may risk legal action or the sack if
this happens.

Your site can be paralyzed through a _simple_ I<denial of service>
(DoS) attack.

Whatever you do, as long as you are connected to the network your site
will be vulnerable. Cut the connections, turn off your machine and put
it into a safe.  Now it is protected--but useless.

So what can you do?

Let's first get acquainted with some security related terminology:

=over 4

=item Authentication

When you want to make sure that a user is who he claims to be, you
generally ask her for a username and a password. Once you have both,
you can check them against your L<database of username/password
pairs|databases/>.  If they match, the user has passed the
B<Authentication> stage. From now on if you keep the
L<session|modules/Apache_Session_Maintain_sessi> open all you need to
do is to remember the username.

=item Authorization

You might want to allow user B<foo> to have access to some resource,
but restrict her from accessing another resource, which in turn is
accessible only for user B<bar>. The process of checking access rights
is called B<Authorization>. For B<Authorization> all you need is an
authenticated username or some other attribute which you can authorize
against. For example, you can authorize against IP number, allowing only
your local users to use some service. But be warned that IP numbers or
session_ids can be spoofed (forged), and that is why you should not do
B<Authorization> without B<Authentication>.


=back

Actually you've been familiar with both these concepts for a while.

When you telnet to your account on some machine you go through a login
process (B<Authentication>).

When you try to read some file from your file systems, the kernel
checks the permissions on this file (B<Authorization>). You may hear
about B<Access control> which is another name for the same thing.


=head1 Illustrated Security Scenarios

I am going to present some real world security requirements and their
implementations.

=head2 Non authenticated access for internal IPs, Authenticated for external IPs

An B<Extranet> is very similar to an B<Intranet>, but at least partly
accessible from outside your organization. If you run an Extranet you
might want to let your internal users have unrestricted access to your
web server. If these same users call from outside your organization
you might want to make sure that they are in fact your employees.

These requirements are achieved very simply by putting the IP
patterns of the organization in a Perl Access Handler in an
C<.htaccess> file.  This sets the REMOTE_USER environment variable
to the organization's generic username. Scripts can test the
C<REMOTE_USER> environment variable to determine whether to allow
unrestricted access or else to require authentication.

Once a user passes the authentication stage, either bypassing it because
of his IP address or after entering a correct login/password pair, the
C<REMOTE_USER> variable is set. Then we can talk about authorization.

Let's see the implementation of the authentication stage. First we
modify <httpd.conf>:

  PerlModule My::Auth
  
  <Location /private>
    PerlAccessHandler My::Auth::access_handler
    PerlSetVar Intranet "10.10.10.1 => userA, 10.10.10.2 => userB"
    PerlAuthenHandler My::Auth::authen_handler
    AuthName realm
    AuthType Basic
    Require valid-user
    Order deny, allow
    Deny from all
  </Location>

Now the code of My/Auth.pm:

    sub access_handler {
  
        my $r = shift;
  
        unless ($r->some_auth_required) {
                $r->log_reason("No authentication has been configured");
                return FORBIDDEN;
        }
        # get list of IP addresses
        my %ips = split /\s*(?:=>|,)\s*/, $r->dir_config("Intranet");
  
        if (my $user = $ips{$r->connection->remote_ip}) {
  
                # update connection record
                $r->connection->user($user);
  
                # do not ask for a password
                $r->set_handlers(PerlAuthenHandler => [\&OK]);
        }
        return OK;
    }
     
    sub authen_handler {
  
        my $r = shift;
  
        # get user's authentication credentials
        my ($res, $sent_pw) = $r->get_basic_auth_pw;
        return $res if $res != OK;
        my $user = $r->connection->user;
  
        # authenticate through DBI
        my $reason = authen_dbi($r, $user, $sent_pw);
  
        if ($reason) {
                $r->note_basic_auth_failure;
                $r->log_reason($reason, $r->uri);
                return AUTH_REQUIRED;
        }
        return OK;
    }
     
    sub authen_dbi{
      my ($r, $user, $sent_pw) = @_;
  
      # validate username/passwd
  
      return 0 if (*PASSED*) # replace with real code!!!
  
      return "Failed for X reason";
  
    }
    # don't forget 1;
    1;

You can implement your own C<authen_dbi()> routine, or you can replace
C<authen_handler()> with an existing authentication handler such as
C<Apache::AuthenDBI>.

If one of the IP addresses is matched, C<access_handler()> sets
C<REMOTE_USER> to be either C<userA> or C<userB>.

If neither IP address is matched, C<PerlAuthenHandler> will not be set
to OK, and the Authentication stage will ask the user for a login and
password.


=head1 Authentication code snippets

=head2 Forcing re-authentication

To force an authenticated user to reauthenticate just send the following
header to the browser:

  WWW-Authenticate: Basic realm="My Realm"
  HTTP/1.0 401 Unauthorized

This will pop-up (in Netscape at least) a window saying
B<Authorization Failed.  Retry?> with B<OK> and a B<Cancel> buttons.
When that window pops up you know that the password has been
discarded.  If the user hits the B<Cancel> button the username will
also be discarded. If she hits the B<OK> button, the authentication
window will be brought up again with the previous username already in
place.

In the Perl API you would use the note_basic_auth_failure() method to
force reauthentication.

This may not work!  The browser's behaviour is in no way guaranteed.


=head2 OK, AUTH_REQUIRED and FORBIDDEN in Authentication handlers

When your authentication handler returns OK, it means that user has
correctly authenticated and now C<$r-E<gt>connection-E<gt>user> will
have the username set for subsequent requests. For C<Apache::Registry>
and friends, where the environment variable settings weren't erased,
an equivalent C<$ENV{REMOTE_USER}> variable will be available. 

The password is available only through the Perl API with the help of
the get_basic_auth_pw() method.

If there is a failure, unless it's the first time, the C<AUTH_REQUIRED>
flag will tell the browser to pop up an authentication window, to try
again. For example:

   my($status, $sent_pw) = $r->get_basic_auth_pw;
   unless($r->connection->user and $sent_pw) {
       $r->note_basic_auth_failure;
       $r->log_reason("Both a username and password must be provided");
       return AUTH_REQUIRED;
   }

Let's say that you have a mod_perl authentication handler, where the
user's credentials are checked against a database.  It returns either
C<OK> or C<AUTH_REQUIRED>. One of the possible authentication failure
case might happen when the username/password are correct, but the
user's account has been suspended temporarily.

If this is the case you would like to make the user aware of this, by
displaying a page, instead of having the browser pop up the
authentication dialog again. You will also refuse authentication, of
course.

The solution is to return C<FORBIDDEN>, but before that you should set
a custom error page for this specific handler, with help of
C<$r-E<gt>custom_response>. It looks something like this:

  use Apache::Constants qw(:common);
  $r->custom_response(SERVER_ERROR, "/errors/suspended_account.html");
   
  return FORBIDDEN if $suspended;


=head1 Apache:Auth* modules

=over 

=item * PerlAuthenHandler's

  Apache::AuthAny           Authenticate with any username/password 
  Apache::AuthenCache       Cache authentication credentials        
  Apache::AuthCookie        Authen + Authz via cookies              
  Apache::AuthenDBI         Authenticate via Perl's DBI             
  Apache::AuthExpire        Expire Basic auth credentials           
  Apache::AuthenGSS         Generic Security Service (RFC 2078)     
  Apache::AuthenIMAP        Authentication via an IMAP server       
  Apache::AuthenPasswdSrv   External authentication server          
  Apache::AuthenPasswd      Authenticate against /etc/passwd        
  Apache::AuthLDAP          LDAP authentication module              
  Apache::AuthPerLDAP       LDAP authentication module (PerLDAP)    
  Apache::AuthenNIS         NIS authentication                      
  Apache::AuthNISPlus       NIS Plus authentication/authorization   
  Apache::AuthenRaduis      Authentication via a Radius server      
  Apache::AuthenSmb         Authenticate against NT server          
  Apache::AuthenURL         Authenticate via another URL            
  Apache::DBILogin          Authenticate to backend database        
  Apache::DCELogin          Obtain a DCE login context              
  Apache::PHLogin           Authenticate via a PH database          
  Apache::TicketAccess      Ticket based access/authentication      

=item  * PerlAuthzHandler's

  Apache::AuthCookie        Authen + Authz via cookies              
  Apache::AuthzAge          Authorize based on age                  
  Apache::AuthzDCE          DFS/DCE ACL based access control        
  Apache::AuthzDBI          Group authorization via Perl's DBI      
  Apache::AuthzGender       Authorize based on gender               
  Apache::AuthzNIS          NIS authorization                       
  Apache::AuthzPasswd       Authorize against /etc/passwd           
  Apache::AuthzSSL          Authorize based on client cert          
  Apache::RoleAuthz         Role-based authorization                

=item * PerlAccessHandler's

  Apache::AccessLimitNum    Limit user access by number of requests 
  Apache::BlockAgent        Block access from certain agents        
  Apache::DayLimit          Limit access based on day of week       
  Apache::IPThrottle        Limit bandwith consumption by IP        
  Apache::RobotLimit        Limit access of robots                  
  Apache::SpeedLimit        Control client request rate             

=back

=cut
