Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "authentication"

sfGuard Logging with Brute Force Login Prevention

For those of us who just need a bit more security out of this already awesome authentication plugin. This has been tested to work on symfony 1.2 using sfGuardPlugin version 3.1.3 stable.

The default behavior of this snippet is a log that tracks each login attempt, the IP, the username entered, and whether it was successful or not. The brute force protection's default settings will check to see if more than 5 attempts for the same username in the past 2 minutes were failed attempts. It also includes a cooldown period of 15 minutes from the last failed login attempt. All of these variables can be changed in the arguments for checkBruteForce().

The first thing we need to do is create a log for sfGuard. Your schema should now contain the following:

plugins/sfGuardPlugin/config/schema.yml:

sf_guard_log:
    _attributes:    { phpName: sfGuardLog }
    id:             
    created_at:     
    ip:             varchar(30)
    username:       varchar(20)
    valid_login:    boolean
 

Once the changes to your schema have been made, rebuild your models, forms, and filters. Next, we need to extend the peer class for this new sfGuardLog model to handle brute force protection.

plugins/sfGuardPlugin/lib/model/sfGuardLogPeer.php:

/*
    Auther: Christopher Lewis (chris@bluehousegroup.com)
 
    Check for brute force login attempts
 
    Case I
     1. Get all logins for this username in the past N minutes
     2. If P or more of them are fails, this is likely a brute force attempt, return true
 
    Case II
     1. Get last P logins
     2. Are they all fails? AND Are they all within the same window of N minutes? AND Was the last attempt within the past Q minutes?
        If all are true, return true
 
    Where:
     N = $attempt_window
     P = $allowed_fail_count
     Q = $cooldown_window
  */
  public static function checkBruteForce($username, $attempt_window = 2, $cooldown_window = 15, $allowed_fail_count = 5)
  {
    //Case I...
 
    $N_minutes_ago_timestamp = strtotime("$attempt_window minutes ago");
    $N_minutes_ago_sqltime = date('Y-m-d H:i:s', $N_minutes_ago_timestamp);
 
    $c = new Criteria();
    $c->add(sfGuardLogPeer::USERNAME, $username);
    $c->add(sfGuardLogPeer::CREATED_AT, $N_minutes_ago_sqltime, Criteria::GREATER_THAN);
    $entries = sfGuardLogPeer::doSelect($c);
 
    $failed_count = 0;
    foreach($entries as $log) { if($log->getValidLogin() == false) { $failed_count++; } }
 
    if($failed_count >= $allowed_fail_count) { return true; }
 
    //Case II...
 
    $all_fails = false; $all_in_window = false; $last_attempt_recent = false;
 
    //Are they all fails?
    $c = new Criteria();
    $c->add(sfGuardLogPeer::USERNAME, $username);
    $c->addDescendingOrderByColumn(sfGuardLogPeer::ID);
    $c->setLimit($allowed_fail_count);
 
    $failed_count = 0; $timestamps = array();
    $entries = sfGuardLogPeer::doSelect($c);
    foreach($entries as $pos => $log)
    {
      if($log->getValidLogin() == false) { $failed_count++; }
      array_push($timestamps, strtotime($log->getCreatedAt()));
    }
 
    if($failed_count >= $allowed_fail_count) { $all_fails = true; }
    else { return false; }
 
    //Are they all within the same window of N minutes?
    sort($timestamps);
    $time_a = $timestamps[0];
    $time_b = $timestamps[(count($timestamps) - 1)];
    $window_in_secs = $time_b - $time_a;
    $window_in_mins = $window_in_secs / 60;
 
    if($window_in_mins <= $attempt_window) { $all_in_window = true; }
    else { return false; }
 
    //Was the last attempt less than Q minutes ago?
    $Q_minutes_ago_timestamp = strtotime("$allowed_fail_count minutes ago");
    $last_attempt_timestamp = strtotime($entries[0]->getCreatedAt());
    if($Q_minutes_ago_timestamp <= $last_attempt_timestamp) { $last_attempt_recent = true; }
    else { return false; }
 
    //If all are true, return true
    if($all_fails == true && $all_in_window == true && $last_attempt_recent == true) { return true; }
 
    return false;  
  }
 

Last, we'll need to overload the executeSignin action in the sfGuardAuth module to include our new logging and security features:

plugins/sfGuardPlugin/modules/sfGuardAuth/actions/actions.class.php:

public function executeSignin($request)
  {
    $user = $this->getUser();
    if($user->isAuthenticated())
    {
      return $this->redirect('@homepage');
    }
 
    $class = sfConfig::get('app_sf_guard_plugin_signin_form', 'sfGuardFormSignin');
    $this->form = new $class();
 
    if($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('signin'));
      $values = $this->form->getValues();
 
      //Log attempts
      $log = new sfGuardLog();
      $log->setIp($_SERVER['REMOTE_ADDR']);
      $log->setUsername($request->getParameter('signin[username]'));
 
      //Check for brute force attempt
      $is_brute_force = sfGuardLogPeer::checkBruteForce($request->getParameter('signin[username]'));
 
      if($this->form->isValid() && !($is_brute_force))
      { 
        $this->getUser()->signin($values['user'], array_key_exists('remember', $values) ? $values['remember'] : false);
 
        // always redirect to a URL set in app.yml
        // or to the referer
        // or to the homepage
        $signinUrl = sfConfig::get('app_sf_guard_plugin_success_signin_url', $user->getReferer('@homepage'));
 
        $log->setValidLogin(true);
        $log->save();
 
        return $this->redirect($signinUrl);
      }
      else
      { 
        $log->setValidLogin(false);
        $log->save();
      }
    }
  }
 
by Christopher Lewis on 2009-06-03, tagged authentication  brute  login  plugins  security  sfguard 

How to add HTTP Auth to symfony

Here is a little hack to use http auth when credentials or auth is insufficient:

public function executeSecure()
  {
    if (!$this->getUser()->hasAttribute("secure_referer"))
        $this->getUser()->setAttribute("secure_referer", $this->getRequest()->getReferer());
 
    if (!isset($_SERVER['PHP_AUTH_USER']))
    {
      header('WWW-Authenticate: Basic realm="Member Area"');
      header('HTTP/1.0 401 Unauthorized');
 
      return sfView::NONE;
    }
    else
    {   
        if ($this->getUser()->tryLogin($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']))
        {
            return $this->redirect($this->getUser()->getAttribute("secure_referer"));
        }
        else
        {
          header('WWW-Authenticate: Basic realm="Member Area"');
          header('HTTP/1.0 401 Unauthorized');
 
          return sfView::NONE;
        }
    }
  }

No template is needed, as everytime you access it will redirect to the referer. Then change in app/yourapp/config/settings.yml the secure_module and secure_action to match this module.

You will need a myUser::tryLogin function that returns a boolean saying "auth is ok" or "bad auth"

And then you're done :p

[from my Wiki Post ab out that]

by Romain Dorgueil on 2006-05-25, tagged authentication  credentials  http  user 
(7 comments)