PHP Classes

File: DAP.php

Recommend this page to a friend!
  Classes of Craig Manley   Authen_DAP   DAP.php   Download  
File: DAP.php
Role: Class source
Content type: text/plain
Description: The main class, based on an existing Perl class of mine.
Class: Authen_DAP
Slow down attempts to guess login passwords
Author: By
Last change:
Date: 19 years ago
Size: 9,942 bytes
 

Contents

Class file image Download
<?php
// +----------------------------------------------------------------------+
// | Dictionary Attack Protection class for PHP5. |
// | Copyright (C) 2005 Craig Manley |
// +----------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU Lesser General Public License as |
// | published by the Free Software Foundation; either version 2.1 of the |
// | License, or (at your option) any later version. |
// | |
// | This library is distributed in the hope that it will be useful, but |
// | WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// | Lesser General Public License for more details. |
// | |
// | You should have received a copy of the GNU Lesser General Public |
// | License along with this library; if not, write to the Free Software |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// | USA |
// | |
// | LGPL license URL: http://opensource.org/licenses/lgpl-license.php |
// +----------------------------------------------------------------------+
// | Author: Craig Manley |
// +----------------------------------------------------------------------+
//
// $Id: DAP.php,v 1.1 2005/01/09 22:44:19 cmanley Exp $
//



/**
 * @author Craig Manley
 * @copyright Copyright © 2004, Craig Manley. All rights reserved.
 * @package com.craigmanley.classes.authen.DAP
 * @version $Revision: 1.1 $
 */




/**
 * Offers protection against dictionary attacks.
 *
 * @package com.craigmanley.classes.authen.DAP
 */
class Authen_DAP {

 
// Private members
 
private $shm = null; // Object implementing IPC_ISharedMem interface.
 
private $max_attempts = null;
  private
$period = null;


 
/**
   * Constructor.
   *
   * @param object $shm shared memory object that implements the IPC_ISharedMem interface.
   * @param integer $max_attempts maximum failed (login) attempts after which to block the identity, default 3.
   * @param integer $period time in seconds to block an identity for after too many failed access attempts, default 180.
   */
 
public function __construct($shm, $max_attempts = 3, $period = 180) {
    if (!(isset(
$shm) && ($shm instanceof IPC_ISharedMem))) {
      throw
Exception('You must pass a shared memory management object that implements the IPC_ISharedMem interface as the 1st parameter!');
    }
   
$this->shm = $shm;
   
$this->max_attempts($max_attempts);
   
$this->period($period);
  }


 
/**
   * Checks if the parameter is a positive integer (representation).
   *
   * @param mixed $value
   * @return boolean
   */
 
protected function _is_pos_int($value) {
    return (
is_int($value) || (is_string($value) && preg_match('/^\+?\d+$/', $value))) && ($value > 0);
  }


 
/**
   * Gets or sets the maximum number of failed access attempts after which an identity
   * will be temporarily blocked. The default value is 3.
   *
   * @param value $name Optional new value. Must be an integer >= 1.
   * @return integer
   */
 
public function max_attempts() {
    if (
func_num_args()) {
     
$value = func_get_arg(0);
      if (!(isset(
$value) && $this->_is_pos_int($value))) {
        throw
Exception("Method parameter is not a positive integer!");
      }
     
$this->max_attempts = intval($value);;
    }
    return
$this->max_attempts;
  }


 
/**
   * Gets or sets the period (in seconds) that identities are blocked for after
   * too many failed access attempts. The default value is 180.
   *
   * @param value $name Optional new value. Must be an integer >= 1.
   * @return integer
   */
 
public function period() {
    if (
func_num_args()) {
     
$value = func_get_arg(0);
      if (!(isset(
$value) && $this->_is_pos_int($value))) {
        throw
Exception("Parameter is not a positive integer!");
      }
     
$this->period = intval($value);;
    }
    return
$this->period;
  }


 
/**
   * Checks to see if the identity's access has been blocked using the given record structure.
   * Returns the seconds the identity is (still) blocked for (0 means not blocked).
   *
   * @param scalar $identity Anything used to identify someone such as an IP, alias, etc.
   * @param array $records array of [$identity, $last_attempt] arrays.
   * @return integer
   */
 
private function _blocked($identity, &$records) {
   
$attempts = 0;
   
$max_attempts = $this->max_attempts();
   
$last_attempt = null;
   
// Search for the last $max_attempts records if any.
   
for ($i = count($records) - 1; $i >= 0; $i--) {
      if (
$records[$i][0] == $identity) {
       
$attempts++;
        if (
is_null($last_attempt)) {
         
$last_attempt = $records[$i][1];
        }
        if (
$attempts >= $max_attempts) {
          break;
        }
      }
    }
   
$block_for = null;
    if (
$attempts >= $max_attempts) {
     
$block_for = $last_attempt + $this->period() - time();
      if (
$block_for < 0) { // could happen in theory if code executes slowly.
       
$block_for = 0;
      }
    }
    else {
     
$block_for = 0;
    }
    return
$block_for;
  }


 
/**
   * Deletes expired records from the record structure passed as parameter.
   * Returns the seconds the identity is (still) blocked for (0 means not blocked).
   * Returns the amount of records deleted.
   *
   * @param array $records array of [identity, time] arrays.
   * @return integer
   */
 
private function _delete_expired_records(&$records) {
   
$result = 0;
   
$expire_before = time() - $this->period();
    while (
count($records)) {
      if (
$records[0][1] <= $expire_before) {
       
$result++;
       
array_shift($records);
      }
      else {
        break;
      }
    }
    return
$result;
  }


 
/**
   * Loads the recorded failed access events structure from shared memory,
   * cleans out all expired records if any, saves the structure back into
   * shared memory (if changed), and returns the new record structure.
   *
   * @return array
   */
 
private function &_records() {
   
$records = null;
   
$shm = $this->shm;
   
$shm->transaction_start();
    try {
     
$s = $shm->fetch();
      if (isset(
$s) && strlen($s)) {
         
$records = unserialize($s);
        if (
$this->_delete_expired_records($records)) {
         
$s = serialize($records);
         
$shm->store($s);
        }
      }
     
$shm->transaction_finish();
    }
    catch(
Exception $e) {
     
$shm->transaction_finish();
      throw
$e;
    }
    if (!isset(
$records)) {
     
$records = array();
    }
    return
$records;
  }


 
/**
   * Records a failed access attempt and returns the number of seconds the identity
   * has been blocked for (0 meaning not blocked).
   *
   * @param string $identity Anything used to identify somebody such as an IP address, login alias, session key, etc.
   * @return integer
   */
 
public function record_failed_attempt($identity) {
   
$records = null;
   
$shm = $this->shm;
   
$shm->transaction_start();
    try {
     
$s = $shm->fetch();
      if (isset(
$s) && strlen($s)) {
         
$records = unserialize($s);
       
$this->_delete_expired_records($records);
      }
      else {
       
$records = array();
      }
     
array_push($records, array($identity, time()));
     
$s = serialize($records);
     
$shm->store($s);
     
$shm->transaction_finish();
    }
    catch(
Exception $e) {
     
$shm->transaction_finish();
      throw
$e;
    }
    return
$this->_blocked($identity,$records);
  }


 
/**
   * Dumps the records currently stored in shared memory to stdout. Only useful for debugging.
   */
 
public function dump_records() {
   
$records = $this->_records();
   
print_r($records);
  }


 
/**
   * Clears all interal records of failed access attempts for the given identity.
   *
   * @param string $identity Anything used to identify somebody such as an IP address, login alias, session key, etc.
  */
 
public function clear($identity) {
   
$shm = $this->shm;
   
$shm->transaction_start();
    try {
     
$s = $shm->fetch();
      if (isset(
$s) && strlen($s)) {
       
$records = unserialize($s);
       
$changed = false;
        if (
$this->_delete_expired_records($result)) {
         
$changed = true;
        }
       
$i = 0;
        while (
count($records) && ($i < count($records))) {
          if (
$records[$i][0] == $identity) {
           
array_splice($records,$i,1);
           
$changed = true;
          }
          else {
           
$i++;
          }
        }
        if (
$changed) {
         
$s = serialize($result);
         
$shm->store($s);
        }
      }
     
$shm->transaction_finish();
    }
    catch(
Exception $e) {
     
$shm->transaction_finish();
      throw
$e;
    }
  }


 
/**
   * Checks to see if the indentity's access has been blocked.
   * Returns the number of seconds the identity has been blocked for (0 means not blocked).
   *
   * @param string $identity Anything used to identify somebody such as an IP address, login alias, session key, etc.
   * @return integer
   */
 
public function blocked($identity) {
    return
$this->_blocked($identity, $this->_records());
  }

}


?>