1

I am having an issue with something I have already posted but I thought I would ask the problem again as I have more code with it now.

The ORIGINAL code that I have used for the tutorial

function checkLoggedIn($page)
{
   $loginDiv = '';
   $action = '';
   if (isset($_POST['action']))
   {
      $action = stripslashes ($_POST['action']);
   }

   session_start ();

   // Check if we're already logged in, and check session information against cookies
   // credentials to protect against session hijacking
   if (isset ($_COOKIE['project-name']['userID']) &&
       crypt($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'],
             $_COOKIE['project-name']['secondDigest']) ==
       $_COOKIE['project-name']['secondDigest'] &&
       (!isset ($_COOKIE['project-name']['username']) ||
        (isset ($_COOKIE['project-name']['username']) &&
         Users::checkCredentials($_COOKIE['project-name']['username'],
                                 $_COOKIE['project-name']['digest']))))
   {
      // Regenerate the ID to prevent session fixation
      session_regenerate_id ();

      // Restore the session variables, if they don't exist
      if (!isset ($_SESSION['project-name']['userID']))
      {
         $_SESSION['project-name']['userID'] = $_COOKIE['project-name']['userID'];
      }

      // Only redirect us if we're not already on a secured page and are not
      // receiving a logout request
      if (!isSecuredPage ($page) &&
          $action != 'logout')
      {
         header ('Location: ./');

         exit;
      }
   }
   else
   {
      // If we're not already the login page, redirect us to the login page
      if ($page != Page::LOGIN)
      {
         header ('Location: login.php');

         exit;
      }
   }

   // If we're not already logged in, check if we're trying to login or logout
   if ($page == Page::LOGIN && $action != '')
   {
      switch ($action)
      {
         case 'login':
         {
            $userData = Users::checkCredentials (stripslashes ($_POST['login-username']),
                                                 stripslashes ($_POST['password']));
            if ($userData[0] != 0)
            {
               $_SESSION['project-name']['userID'] = $userData[0];
               $_SESSION['project-name']['ip'] = $_SERVER['REMOTE_ADDR'];
               $_SESSION['project-name']['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
               if (isset ($_POST['remember']))
               {
                  // We set a cookie if the user wants to remain logged in after the
                  // browser is closed
                  // This will leave the user logged in for 168 hours, or one week
                  setcookie('project-name[userID]', $userData[0], time () + (3600 * 168));
                  setcookie('project-name[username]',
                  $userData[1], time () + (3600 * 168));
                  setcookie('project-name[digest]', $userData[2], time () + (3600 * 168));
                  setcookie('project-name[secondDigest]',
                  DatabaseHelpers::blowfishCrypt($_SERVER['REMOTE_ADDR'] .
                                                 $_SERVER['HTTP_USER_AGENT'], 10), time () + (3600 * 168));
               }
               else
               {
                  setcookie('project-name[userID]', $userData[0], false);
                  setcookie('project-name[username]', '', false);
                  setcookie('project-name[digest]', '', false);
                  setcookie('project-name[secondDigest]',
                  DatabaseHelpers::blowfishCrypt($_SERVER['REMOTE_ADDR'] .
                                                 $_SERVER['HTTP_USER_AGENT'], 10), time () + (3600 * 168));
               }

               header ('Location: ./');

               exit;
            }
            else
            {
               $loginDiv = '<div id="login-box" class="error">The username or password ' .
                           'you entered is incorrect.</div>';
            }
            break;
         }
         // Destroy the session if we received a logout or don't know the action received
         case 'logout':
         default:
         {

            // Destroy all session and cookie variables
            $_SESSION = array ();
            setcookie('project-name[userID]', '', time () - (3600 * 168));
            setcookie('project-name[username]', '', time () - (3600 * 168));
            setcookie('project-name[digest]', '', time () - (3600 * 168));
            setcookie('project-name[secondDigest]', '', time () - (3600 * 168));

            // Destory the session
            session_destroy ();

            $loginDiv = '<div id="login-box" class="info">Thank you. Come again!</div>';

            break;
         }
      }
   }

   return $loginDiv;
}

My code:

<?php

function encrypt($input)
{
$hash = password_hash($input, PASSWORD_DEFAULT);
return $hash;
}

function checkUserCreds($username, $password)
{
    $id = 0;
    $hash = '';

    $db = new PDO('$dbDNS', '$dbuser', '$dbpass');
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //Set error mode
    try
    {
        $st = $db->prepare("SELECT id, login, email, pass FROM users WHERE login =:username");      
        $st->bindParam(':username', $username, PDO::PARAM_STR);
        $success = $st->execute();

        if($success)
        {
            $userData = $st->fetch();
            $hash = $userData['pass'];
            if (password_verify($password, $hash) == $hash)
            {
                $id = $userData['id'];
            }           
        }

    }
    catch (PDOException $e)
    {
        $id = 0;
        $hash = '';
    }
    $db = null;

    return array ($id, $username, $hash);
}

function checkLoggedIn($page)
{
    $loginMess='';
    $action='';
    if (isset($_POST['action']))
    {
        $action = stripslashes($_POST['action']);
    }
    session_start();

    //Check if already logged in and check session information against cookies
    if (isset($_COOKIE['sukd']['id']) && encrypt($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']) == $_COOKIE['sukd']['hashv2'] && (!isset ($_COOKIE['sukd']['username']) || (isset ($_COOKIE['sukd']['username']) && checkUserCreds($_COOKIE['sukd']['username'], $_COOKIE['sukd']['hash']))))
    {
        echo "isset cookies: ON, GOOD <br>";
        // Regenerate the ID to prevent session fixation
        //session_regenerate_id ();
    }   
    else
    {
        // If we are not on the login page, redirect.
        if ($page != 'login')
        {
            header ('Location login.php');
            exit;
        }
    }
    if ($page = 'login' && $action != '')
    {
        switch($action)
        {
            case 'login':
            {
                $userData = checkUserCreds(stripslashes($_POST['username']), stripslashes($_POST['password']));

                if ($userData[0] != 0)
                {
                    $_SESSION['sukd']['id']=$userData[0];
                    $_SESSION['sukd']['ip']=$_SERVER['REMOTE_ADDR'];
                    $_SESSION['sukd']['userAgent']=$_SERVER['HTTP_USER_AGENT'];
                    if(isset($_POST['remember']))
                    {
                        //remember for 7 days
                        setcookie('sukd[id]', $userData[0], time () + (3600 * 168));
                        setcookie('sukd[username]', $userData[1], time() + (3600 * 168));
                        setcookie('sukd[hash]', $userData[2], time() + (3600 * 168));
                        setcookie('sukd[hashv2]', encrypt($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']), time () + (3600 * 168));
                    }
                    else
                    {
                        setcookie('sukd[id]', $userData[0], false);
                        setcookie('sukd[username]', '', false);
                        setcookie('sukd[hash]', '', false);
                        setcookie('sukd[hashv2]', encrypt($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']), time () + (3600 * 168));
                    }

                    header ('Location: ./');

                    exit;
                }
                else
                {
                    $loginMess = "The username or password you entered is incorrect <br>";
                }
                break;              
            }
            case 'logout':
            default:
            {
                $_SESSION = array();
                setcookie('sukd[id]', '', time () + (3600 * 168));
                setcookie('sukd[username]', '', time() + (3600 * 168));
                setcookie('sukd[hash]', '', time() + (3600 * 168));
                setcookie('sukd[hashv2]', '', time () + (3600 * 168));

                session_destroy();

                $loginMess = "echo 'Successfully logged out <br>'";

                break;          
            }       
        }
    }
    return $loginMess;
}
?>

It is called by checkLogged(login) for example and that outputs the login message if there is a problem. In addition it uses a hidden field with action to set the value, login or logout for the case switch. Currently, it logs in fine, adds the cookies etc.

However, the problem is, when a user has already logged in, it should be checking the code.

if (isset($_COOKIE['sukd']['id']) && encrypt($_SERVER['REMOTE_ADDR'] etc..

I couldn't really make much sense of the original code, so I am not even sure where to begin. The cookie array is a bit weird how it seems to be based on two different versions based on whether you setcookie or call the cookie.

If anyone has a more secure without going over the top method, I am happy for someone to enlighten me further on this.

Original to my code.

digest = hash
decondDigest = hashv2
omega_prime
  • 65
  • 3
  • 17

2 Answers2

2

I wouldn't call session_start(); within the function. If you use the cookies anywhere else you would need it there anyway. Have it at the start of your very first file somewhere before anything else.

And perhaps use this:

if (!isset($_SESSION))
  {
    session_start();
  }

If anyone has a more secure without going over the top method, I am happy for someone to enlighten me further on this.

Why not switch to $_SESSIONs?

Using cookies is completely fiddly to try to make it secure and so users cannot set certain data themselves, as you are fighting with now. In contrast, I cannot set a $_SESSION on your server.

Then, on a very basic example:

//your login script
//if logged in successful:
$_SESSION['loggedin']['username']=$username; //from DB
$_SESSION['loggedin']['whatever']=$whatever;

//Then your login check just checks the session
if (!isset($_SESSION['loggedin']))
  {
    //redirect to login page or don't server them user stuff
  }

Then you don't need to bother with hashing data you don't want them to see etc. Depending on your security requirements, you can check and set various things in the sessions.

Importantly, how you currently have it, while you check some specifics in the cookie, people can set their own cookies, which means your code might just check a cookie a user has set and think they're logged in and give them access to things, maybe another user's account.

Sessions, while not 100% secure in the vein that nothing is, are pretty secure as are stored on the server out the web root, which means for someone to fiddle with them, they'd already be in the server, and setting sessions is the last thing they need to do to cause havoc.

James
  • 4,644
  • 5
  • 37
  • 48
  • I would rather use a bit of both, I don't think someone could easily use cookies as I have used a mixture of the IP Address and HTTP User Agent which in itself is hashed and will be checked against the user. The problem is, I can't figure out the isset in the tutorial to match mine. – omega_prime Sep 21 '13 at 12:03
  • So if I set a cookie file on my own machine with my IP address and HTTP User agent, what do you check? Just that it exists in the cookie and matches mine? How is that a security check? I would set my own cookie with whatever `$_SERVER['REMOTE_ADDR']` and `$_SERVER['HTTP_USER_AGENT']` would set. If this is all you do, then when I set my cookie and set `['username']`, I can just choose someone else username and you will allow me in. – James Sep 21 '13 at 12:08
  • Using cookies is completely fiddly to try to make it secure and so users cannot set certain data, as you are fighting with now. In contrast, I cannot set a `$_SESSION` on your server. The session basically matches an ID in a cookie on my side, if there is no cookie match on my side to a session on your server then I have no session (etc). – James Sep 21 '13 at 12:10
  • Well I add ip address + user agent data, hash them together and then compare it. I have put it as though it is a password together but you do raise a good point. To be honest, I don't really need security that much, but sessions, how much impact do they have on the server? Also, don't sessions present a problem when they are on the same server as other people? And don't they just expire when you close the browser? The remember seems to create the cookies, but otherwise it stores as a session, I think. As you can see $hash($input, password_default) is what I use to encrypt user agent+ip address – omega_prime Sep 21 '13 at 12:19
  • It's not terribly easy to guess I agree, but don't I just sign up and login and get a legit cookie from you and fiddle with it to see what you have done? Thee are only so many things you could hash I could try, username, password, time/date, IP, browser. It wouldn't take long to write a quick dirty script that took the legit hash in the cookie you set for me and check it against a range of other hashed things. Also, some people's IPs change (load balanced/shared/other network setup) – James Sep 21 '13 at 14:21
  • as for the session security, have a read of this question and answers for food for thought - http://stackoverflow.com/questions/328/php-session-security (read the comments too) – James Sep 21 '13 at 14:22
0

Found out the reason why it wasn't working. I was rehashing using password_hash when I should have been using password_verify. This meant it gave a different answer each time.

omega_prime
  • 65
  • 3
  • 17
  • great! Another thing to mention here, if you provide a change password option, once they do your login checker will return they are not logged in. You need to handle that cleanly. Ie on password change page update the cookie before the check logged in function (etc). (another reason why doing it the way you are currently is fiddly) – James Sep 21 '13 at 14:44
  • Very true, I just thought it looked quite good, some of the ideas with $action and the likes. I only have done it in java, but it made it look quite cleanly. Pause for thought though on password changes. Cheers. – omega_prime Sep 21 '13 at 15:30