1

As an exercise for class, I'm creating a class that manages user sessions.

The session details are stored in a database and also there is a $_SESSION[user_id] variable to carry in between pages.

The idea is that it would be hard for an attacker to steal the cookie AND the ip address AND the user agent AND to keep the session refreshing, as I put a limit of 15 minutes.

When the user logs in, the database object is created, then its existence is checked in every page that is visited.

<?php
//Recojo parametros de formulario de logeo
$username = $_POST['username'];
$password = $_POST['password'];

//abro conexion base de datos
$mysqli = new mysqli("localhost", "root", "unir_2014", "wordpress");
if ($mysqli->connect_errno) {
    printf("La conexion ha fallado! El servidor responde: %s\n", $mysqli->connect_error);
    exit();
}
//----------------------------------------------------------------------------------------//       
//----------------------------------------------------------------------------------------//     //EXPIRAR         
            $comienzo = time(); // Momento al logearse
            // Le damos un tiempo de expiracion de 1 minuto
            $expirar = $comienzo + (15 * 60);
//----------------------------------------------------------------------------------------//     //IP ADDRESS  
              if (isset($_SERVER)) {
                  if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && ip2long($_SERVER["HTTP_X_FORWARDED_FOR"]) !== false) {
                      $ipadres = $_SERVER["HTTP_X_FORWARDED_FOR"];
                  } elseif (isset($_SERVER["HTTP_CLIENT_IP"])  && ip2long($_SERVER["HTTP_CLIENT_IP"]) !== false) {
                      $ipadres = $_SERVER["HTTP_CLIENT_IP"];
                  } else {
                      $ipadres = $_SERVER["REMOTE_ADDR"];
                  }
              } else {
                  if (getenv('HTTP_X_FORWARDED_FOR') && ip2long(getenv('HTTP_X_FORWARDED_FOR')) !== false) {
                      $ipadres = getenv('HTTP_X_FORWARDED_FOR');
                  } elseif (getenv('HTTP_CLIENT_IP') && ip2long(getenv('HTTP_CLIENT_IP')) !== false) {
                      $ipadres = getenv('HTTP_CLIENT_IP');
                  } else {
                      $ipadres = getenv('REMOTE_ADDR');
                  }
              }
//----------------------------------------------------------------------------------------//      $version_cliente = $_SERVER['HTTP_USER_AGENT'];
//----------------------------------------------------------------------------------------//$query = "SELECT * FROM sesiones WHERE name ='" .$username."'";
if ($result = $mysqli->query($query)) {
    while($row = mysqli_fetch_array($result)) {
        $salt = $row['salt'];
        $passcheck = $row['password'];
        $id_usuario = $row['id_usuario'];
        $passwordin = md5($password.$salt);
        $direccion_ip = $ipadres;
    }
    //compruebo si la contraseña coincide con el hash almacenado en la base de datos
         if( $passcheck === $passwordin){

                echo '<script> window.location = "privatecontent.php" </script>';
                //if the user has javascript

                $url = 'privatecontent.php';
                echo '<META HTTP-EQUIV=Refresh CONTENT="0; URL='.$url.'">';
                //if the user doesnt have javascript*
//----------------------------------------------------------------------------------------//    //INTRODUCIMOS OBJETO SESSION EN LA BASE DE DATOS 
    //mysqli_query($connection, "INSERT INTO `wordpress`.`session_log` (`id_sesion`, `id_usuario`, `direccion_ip`, `version_cliente`, `expirar`) VALUES ('', '".$id_usuario."', '".$direccion_ip."', '".$version_cliente."', '".$expirar."');");
    mysqli_query($mysqli, "INSERT INTO `wordpress`.`session_log` (`id_sesion`, `id_usuario`, `direccion_ip`, `version_cliente`, `expirar`) VALUES ('', '".$id_usuario."', '".$direccion_ip."', '".$version_cliente."', '".$expirar."');"); 
    //la variable de sesion solo almacena la id de usuario en uso, para asociarlo a la sesion de la base de datos
    session_start();
    $_SESSION['user_id'] = $id_usuario;
    }
}
else{    
                 echo '<div id="wrong" class="wrong"> "Nombre de usuario o contraseña incorrectos! <br>';
                 echo "Haz click<a href='FORMULARIO DE REGISTRO!!!'>aqui</a> para registrarte...</div>";  
            }

That would be the login manager, now, the check_login class:

<?php
// TODAS LAS PAGINAS DEBEN INCLUIR ESTE ARCHIVO!
//abro conexion base de datos
$mysqli = new mysqli("localhost", "root", "unir_2014", "wordpress");
if ($mysqli->connect_errno) {
    printf("La conexion ha fallado! El servidor responde: %s\n", $mysqli->connect_error);
    exit();
}

//buscamos el id del usuario activo.
//recibimo de $_SESSION (o sea la cookie del cliente) solo el id del usuario. que usuario dice ser
//si la cookie ha sido robada, lo mas probable es que la ip y el user agent hayan cambiado, asi que la session FALLARA
//robar una cookie antigua tampoco ayudara, pues solo duran 15 minutos
session_start();
$id_usuario = $_SESSION['user_id'];

//busco en la tabla SESIONES si existe un numbre de usuario como ese
$query = "SELECT * FROM session_log WHERE id_usuario ='".$id_usuario."'";

//si existe, saco sus datos
if ($result = $mysqli->query($query)) {
    while($row = mysqli_fetch_array($result)) {
        $ip_check = $row['direccion_ip'];
        $expirar = $row['expirar'];
        $cliente_check = $row['version_cliente'];
        $id_sesion = $row['id_sesion'];
    }
}



//comprobamos que el usuario esta logeado, tiene buen user_agent, la misma direccion ip y no se ha pasado de tiempo

    //COMPROBAR SI LA SESION HA EXPIRADO
        $now = time(); 
        if ($now > $expirar) {
            $url = 'NO_SESSION.php';
            echo '<script> window.location = "'.$url.'" </script>';
            echo '<META HTTP-EQUIV=Refresh CONTENT="0; URL='.$url.'">';
        }

    //COMPROBAR QUE VIENE DE LA MISMA IP   
            if (isset($_SERVER)) {
                  if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && ip2long($_SERVER["HTTP_X_FORWARDED_FOR"]) !== false) {
                      $ipadres = $_SERVER["HTTP_X_FORWARDED_FOR"];
                  } elseif (isset($_SERVER["HTTP_CLIENT_IP"])  && ip2long($_SERVER["HTTP_CLIENT_IP"]) !== false) {
                      $ipadres = $_SERVER["HTTP_CLIENT_IP"];
                  } else {
                      $ipadres = $_SERVER["REMOTE_ADDR"];
                  }
              } else {
                  if (getenv('HTTP_X_FORWARDED_FOR') && ip2long(getenv('HTTP_X_FORWARDED_FOR')) !== false) {
                      $ipadres = getenv('HTTP_X_FORWARDED_FOR');
                  } elseif (getenv('HTTP_CLIENT_IP') && ip2long(getenv('HTTP_CLIENT_IP')) !== false) {
                      $ipadres = getenv('HTTP_CLIENT_IP');
                  } else {
                      $ipadres = getenv('REMOTE_ADDR');
                  }
              }    


        if ($ip_check !== $ipadres){ 
            $url = 'NO_SESSION.php';
            echo '<script> window.location = "'.$url.'" </script>';
            echo '<META HTTP-EQUIV=Refresh CONTENT="0; URL='.$url.'">';
        }



    //COMPROBAR QUE TIENE EL MISMO USER AGENT   
    $version_cliente = $_SERVER['HTTP_USER_AGENT'];    
        if ($cliente_check != $version_cliente){
            $url = 'NO_SESSION.php';
            echo '<script> window.location = "'.$url.'" </script>';
            echo '<META HTTP-EQUIV=Refresh CONTENT="0; URL='.$url.'">';
        }




    //si ninguno de estos controles falla, llego hasta aqui
    //significa que el usuario ha visitado una pagina nueva y sigue con la misma session, asi que debo
    //actualizar el campo "expirar" para que la session dure mas

    $new_expirar =  time() + (15 * 60);
    mysqli_query($mysqli, "UPDATE `wordpress`.`session_log` SET `expirar` = '".$new_expirar."' WHERE `session_log`.`id_sesion` =$id_sesion;");
4
  • I formatted your post, but it was hard. I suggest to post only well-formatted and well-indented code.
    – peterh
    May 14 '14 at 11:46
  • I am very sorry, gonna check the changes. it was my first question, I didnt' really knew how to insert a code block :( Thanks :D I hope it will attract more people to read the question. May 14 '14 at 14:41
  • 1
    Tying sessions to IP address will break approximately 3% of legitimate users. See pages 26 of this link: westpoint.ltd.uk/papers/Paul_Johnston_GSEC.pdf
    – paj28
    May 14 '14 at 15:03
  • Tying sessions to directly to a user ID also seems like a bad idea. Use something large and unpredictable, like a version 4 UUID. May 14 '14 at 17:24
1

Yes, it is secure, but it has the fallback, that your users won't be able to change their ips. For example, if I get home with my android phone, and switch from gprs to wifi, your site will see an ip change, although I don't even need my browser to restart.

3
  • 3
    In certain corporate environments this can get even worse/annoying, since they might have a "cluster" of webproxies but some use this, and some use that IP to connect to elsewhere. Depending on how balancing is done internally you have to relogin every few minutes or can't work at all since every request is coming from elsewhere.
    – PlasmaHH
    May 14 '14 at 12:22
  • The idea is that if you connect from your phone you open another instance of the session object, a user can have 2 sessions at the same time with different ips and user agents, as long as it has logged in with the correct credentials. I was thinking requiring the first 3 octets of the address, instead of the full 4 so the user only has to be in the same subnet. May 14 '14 at 13:43
  • anyway, any suggestions as to how to improve the whole session manager? im very thankful for your help BTW :D May 14 '14 at 13:44
1

The idea is that it would be hard for an attacker to steal the cookie AND the ip address AND the user agent AND to keep the session refreshing,

I just wanted to note that many attacks take place locally, resulting in the attacker likely sharing the same IP address. If you can get the session key, you also can get the browser user agent.

2
  • What would a better way to keep track then? Im lloking for a "good practice" solution that i can implement in future projects. Thanks for your time! May 14 '14 at 14:40
  • Sorry, I don't feel comfortable expressing an opinion on the matter. I just wanted to share what I know to be true. I am sure there are others better qualified to answer. Best of luck! May 15 '14 at 4:01
1

In order to decide whether this is a valid approach or not, you have to consider the use case of the application and what attack you're trying to protect your users against.

In this case, the attack scenario you're trying to protect your user from is someone stealing the session cookie and then reusing it from a different machine. The consequence of the defense you're proposing is that it necessary for users to log on again every time they change IP addresses.

Judging whether or not the advantages of your solution is worth the drawbacks it creates depends a lot on the context. For instance, if you application data is of high value and is typically used for limited period of times in between which you want your users to authenticate every time (e.g. a web banking interface) then the compromise seems perfectly acceptable.

If, on the other hand, you're accessing low value data and you want to minimize the impact on your users (for instance a shopping site web cart), then it would probably be best not to limit the session to the same IP address.

2
  • This is a class exercise, we are replicating an e-commerce website. this function would be in the shopping cart and checkout pages and controllers, apart from input validation of all fields. May 14 '14 at 14:35
  • Then here is your lesson: security is about finding the right balance between functionality, ease of use and security. I explained what are the element that, in your case, you should take into consideration for deciding whether or not to include it.
    – Stephane
    May 14 '14 at 14:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.