Apache-BruteWatch

 view release on metacpan or  search on metacpan

lib/Apache/BruteWatch.pm  view on Meta::CPAN

notification will be sent. Time is in seconds.

=head2 BruteNotify

Email address to which notifications will be sent

=head2 BruteForgive

Failed login attempts will be cleaned up after they are this old. Units
are seconds.

=cut

sub handler {
    my $r = shift;

    # We only care about unauthorized
    return OK unless $r->status == 401;

    my $user  = $r->dir_config('BruteDataUser');
    my $pass  = $r->dir_config('BruteDataPassword');
    my $dbase = $r->dir_config('BruteDatabase');

    my $dbh =
      DBI->connect( $dbase, $user, $pass,
        { RaiseError => 1, AutoCommit => 1, PrintError => 1 } )
      or die $DBI::errstr;

    my $time     = time;
    my $username = $r->user;
    my $sth      = $dbh->prepare(
        "INSERT into bruteattempt (ts ,
        username) values (?,?)"
    );
    $sth->execute( $time, $username );
    $sth->finish;

    my $count = attacks( $r, $username, $time, $dbh );

    my $maxtries = $r->dir_config('BruteMaxTries');
    if ( $count > $maxtries ) {
        warn(
"Apache::BruteWatch : It appears that $username is under attack. Notification sent."
        );
        notify( $r, $username, $dbh );
    }

    return OK;
}

sub attacks {
    my ( $r, $username, $time, $dbh ) = @_;
    my $count;

    my $old = $r->dir_config('BruteMaxTime');
    my $sth = $dbh->prepare(
        "select count(ID) from bruteattempt where
                username = ? and ts > $time - $old"
    );
    $sth->execute($username);
    $sth->bind_columns( \$count );
    $sth->fetch;
    $sth->finish;

    my $forgive = $r->dir_config('BruteForgive');
    $sth = $dbh->prepare(
        "delete from bruteattempt where 
                          ts > $time - $forgive"
    );
    $sth->execute;
    $sth->finish;

    return $count;
}

sub notify {
    my ( $r, $username, $dbh ) = @_;
    my $count;

    warn "Attempting to notify";

    # Have they already been notified?
    my $sth = $dbh->prepare(
        "select count(ID) from brutenotified
        where username = ?"
    );
    $sth->execute($username);
    $sth->bind_columns( \$count );
    $sth->fetch;
    $sth->finish;

    return if $count;

    my $notify = $r->dir_config('BruteNotify');

    my $message = qq~
    Apache::BruteWatch

    It appears that the username $username is under a brute-force
    password attack.
    ~;

    my %mail = (
        To      => $notify,
        From    => $notify,
        Subject => "User $username under attack",
        Message => $message,
    );
    Mail::Sendmail::sendmail(%mail);

    $sth = $dbh->prepare(
        "insert into brutenotified
        (username, ts )
        values
        (?,?) "
    );
    $sth->execute( $username, time );
    $sth->finish;

    return;
}

=head1 Database

The following is a mysql database table create statement. You'll need to
make the appropriate change to run this on some other database.

  CREATE TABLE bruteattempt (
  ID int(11) NOT NULL auto_increment,
  ts int(11) default NULL,
  username varchar(255) default NULL,
  PRIMARY KEY  (ID)
 ) TYPE=MyISAM;

 CREATE TABLE brutenotified (
  ID int(11) NOT NULL auto_increment,
  username varchar(255) default NULL,
  ts int(11) default NULL,
  PRIMARY KEY  (ID)
 ) TYPE=MyISAM;

=head1 AUTHOR

	Rich Bowen
	rbowen@rcbowen.com
	http://www.cre8tivegroup.com
 
=head1 DATE



( run in 0.531 second using v1.01-cache-2.11-cpan-2398b32b56e )