Uncategorized

PHP login handling tutorial – sessions & cookies included

I see a lot of people trying to write code to authenticate users logging into a PHP website. This is some code I cobbled together in december of 2008 and it has worked problem free since then.

// I've already sanitized all GET, POST, and COOKIE data at this point.
function check_login() {
  global $DB;

  $login_justnow=false;

  // if the user isn't logged in and they're POSTing a login request, process it
  if(!get_session('user/id') && isset($_POST['login']) {
    $name=$_POST['login_name'];  $remember_me=isset($_POST['remember_me'])?1:0;
    $pass=md5($_POST['login_pass']);
    $user_id=$DB->QueryXY("SELECT id FROM `users` WHERE name='$name' AND pass='$pass' AND confirmed='1' LIMIT 1");
    if(isset($user_id)) {
      account_login($user_id,$remember_me);
      $login_justnow=true;
      $name=get_session("user/given_name")?', '.get_session("user/given_name"):'';
      add_notice("Welcome$name!");
    } else {
      add_error("Login failed.");
      account_logout();
    }
  }

  // if the user isn't logged in but has a COOKIE, process it
  if(!get_session("user/id") && isset($_COOKIE["remember_me"]) ) {
    list($user_id,$cookie_code)=@unserialize(stripslashes($_COOKIE["remember_me"]));
    if(isset($user_id) && isset($cookie_code)) {
      $cookie=md5($cookie_code);
      $result=$DB->QueryArray("SELECT * FROM `users` WHERE id='$user_id' AND cookie='$cookie' AND confirmed='1' LIMIT 1");
      if(count($result)) {
        account_login($user_id,true);
        $login_justnow=true;
        $name=get_session("user/given_name")?', '.get_session("user/given_name"):'';
        add_notice("Welcome$name!");
      }
    }
  }

  // if the user's session says they're logged in, process it
  if(get_session("user/id") && $login_justnow===false) {
    $user_id=get_session("user/id");
    $cookie=get_session("user/cookie");
    $ip=get_session("user/ip");
    $session=session_id();

    $query="SELECT * FROM `users` WHERE id='$user_id' AND ip='$ip' AND session='$session' AND cookie='$cookie' AND confirmed='1' LIMIT 1";
    $result=$DB->DoQuery($query);

    if($DB->NumRows($result)) {
      account_login($user_id,false);
    } else {
      add_error("Session security failed.");
      account_logout();
    }
    $DB->EndQuery($result);
  }

  // check if the user actually has rights to this part of the site - your implementation may vary
}

function account_login($user_id,$remember_me) {
  global $DB;

  if(user_is_logged_in())
    return;

  // update cookie
  if($remember_me==true) {
    $cookie_code=generate_random_string();
    $cookie_str=serialize(array($user_id, $cookie_code));
    setcookie('remember_me', $cookie_str, time() + 60*60*24*30, '/');
    add_session("user/cookie",$cookie_code);
  } else {
    remove_session("user/cookie");
  }

  // update session security
  $ip=$_SERVER['REMOTE_ADDR'];
  $session=session_id();
  $cookie_code=get_session("user/cookie");
  $cookie=md5($cookie_code);
  $DB->DoQuery("UPDATE `users` SET last_on=NOW(), session='$session'"
    .(($cookie_code!='')?", cookie='".$cookie."'":"")
    .", ip='$ip' WHERE id='$user_id' LIMIT 1");

  // update session info
  $result=$DB->DoQuery("SELECT * FROM `users` WHERE id='$user_id' LIMIT 1");
  $row=$DB->FetchAssoc($result);
  foreach($row as $k=>$v) {
    add_session("user/".$k,$v);
  }
  $DB->EndQuery($result);

  // check if any other part of your system needs to know about a user logging in.
}

function user_is_logged_in() {
 return get_session("user/id")!=0;
}

function generate_random_string($length=32) {
 $random="";
 srand((double)microtime()*1000000);
 $char_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 $char_list.= "abcdefghijklmnopqrstuvwxyz";
 $char_list.= "1234567890";

 for($i=0;$i<$length;++$i) {
 $random.=substr($char_list,(rand()%(strlen($char_list))), 1);
 }

 return $random;
}