Secure sessions

Ask about a PHP problem here.
Post Reply
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Secure sessions

Post by conradk »

Good evening (good god, it's 3 am here xD),

I have always wondered how to build secure sessions. I read some interesting things today and thought I'd put together a little function so as to avoid these issues:

- session fixation
- CSRF attacks

Tell me what you think, as I'm very unsure whether this will secure scripts or not:
class Session {
   
   public function start($domain) {
      session_start();

// In order to avoid CSRF, we check for the REFERER. If the request doesn't come from our own site, then we delete the session to logout the user
// and avoid any unwanted action the user would have performed while logged in.
// Note that if $_GET['logout'] is set in the URL, the user will also be logged out.
      if (strpos($_SERVER['HTTP_REFERER'], 'http://' . $domain) !== 0 ||
          isset($_GET['logout']) ||
          // It is unlikely that a user will use the same session with two different IPs or browsers.
          // This would a sign that someone is attempting session fixation, session theft or something similar
          // So we destroy the session and regenerate a new one so as to avoid session fixation.
          $_SERVER['REMOTE_ADDR'] !== $_SESSION['PREV_REMOTEADDR'] ||
          $_SERVER['HTTP_USER_AGENT'] !== $_SESSION['PREV_USERAGENT']) {
            session_destroy();
            session_regenerate_id();
      }
      
      // We set the first browser and IP used by the user so as to perform the check explained just above
      if(!isset($_SESSION['PREV_USERAGENT'])) $_SESSION['PREV_USERAGENT'] = $_SERVER['HTTP_USER_AGENT'];
      if(!isset($_SESSION['PREV_REMOTEADDR'])) $_SESSION['PREV_REMOTEADDR'] = $_SERVER['REMOTE_ADDR'];
      if(!isset($_SESSION['token'])) $_SESSION['token'] = uniqid(md5(microtime()), true);

// The token is a value that should be included in a hidden field for each form submitted through the site
// in order to avoid CSRF attacks.
      if ($_POST['token'] !== $_SESSION['token']) {
         exit();
      }
   }
}
Thanks, best regards,
CK
User avatar
jacek
Site Admin
Posts: 3262
Joined: Thu May 05, 2011 1:45 pm
Location: UK
Contact:

Re: Secure sessions

Post by jacek »

The referrer can very easily be faked and the hidden token can easily be found just by viewing the page source of any page that has a form on it.

I think that the IP check is the most valuable here as it is the only one that cannot be easily bypassed.
Image
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Re: Secure sessions

Post by conradk »

jacek wrote:The referrer can very easily be faked and the hidden token can easily be found just by viewing the page source of any page that has a form on it.

I think that the IP check is the most valuable here as it is the only one that cannot be easily bypassed.
But the token can only be seen by the logged in user, right ? So no one else than the logged in user can actually no it. Or at least that was what I was aiming at :oops:

Also, how can the referer be faked in a CSRF attack ? That would mean that the regular user would screw himself up on purpose xD right ?
User avatar
jacek
Site Admin
Posts: 3262
Joined: Thu May 05, 2011 1:45 pm
Location: UK
Contact:

Re: Secure sessions

Post by jacek »

conradk wrote:But the token can only be seen by the logged in user, right ? So no one else than the logged in user can actually no it.:
True, but mr attacker might login before trying to do bad stuff.
conradk wrote:Also, how can the referer be faked in a CSRF attack ? That would mean that the regular user would screw himself up on purpose xD right ?
It depends how it was used, but the referrer is just sent by the browser like $_GET or $_POST so it can be easily set by the user. Whether or not anything useful can be done by doing that depends on your code.

I would still say the IP check is the most valuable.
Image
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Re: Secure sessions

Post by conradk »

jacek wrote:
conradk wrote:But the token can only be seen by the logged in user, right ? So no one else than the logged in user can actually no it.:
True, but mr attacker might login before trying to do bad stuff.
I've since changed things a bit. But my scripts works only locally, not on my online server :/ trying to work things out with my hosting provider. The token is somewhat random (it depends on the IP of the visitor, on the time, etc).
conradk wrote:Also, how can the referer be faked in a CSRF attack ? That would mean that the regular user would screw himself up on purpose xD right ?
It depends how it was used, but the referrer is just sent by the browser like $_GET or $_POST so it can be easily set by the user. Whether or not anything useful can be done by doing that depends on your code.

I would still say the IP check is the most valuable.
I've since found something better :) Use session_id($hash) on every page call. $hash is a string that comes from the IP, the browser used, the time. So it changes every hour without a possibility for you to inject the session id.
User avatar
jacek
Site Admin
Posts: 3262
Joined: Thu May 05, 2011 1:45 pm
Location: UK
Contact:

Re: Secure sessions

Post by jacek »

That kind of combines all of those awkward checks into one nice line.

Very good :D

I usually base the session Id on the IP only, which is like just doing the IP check.
Image
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Re: Secure sessions

Post by conradk »

jacek wrote:That kind of combines all of those awkward checks into one nice line.

Very good :D

I usually base the session Id on the IP only, which is like just doing the IP check.
Well I could do that, since the HTTP_USER_AGENT isn't a secure thing to use. But I guess some beginner hackers won't necessarily know how to change the headers. The more things you do to annoy hackers, the most secure it is. At least that's what I think.

On a totally unrelated subject: DAMN :(

I wrote a script and it works locally but not on my hosting account. Could you tell me if you see anything wrong if I showed you the code ? I have a Apache/PHP local server with v3.5.3 on Ubuntu here and the script works just as expected (after having debugged for an hour). Online, the server that I use has php 3.2.17. The script doesn't work. No errors. No DB is used. I'm getting crazy. The hosting support said they're checking the script (that was 4 hours ago, and they usually respond within 10 minutes). I've asked on another forum but no one was able to help me for the moment.

Anyways, here's the script. If you have time, I'd be grateful if you could give it a try. But don't spend too much time on it. I hope my hosting service will figure it out. Maybe try it on your local server, tell me if it works. Put this in a file called /classes/index2.php.
<?php
ini_set('display_errors',1);
error_reporting(E_ALL);


class Session {
   
   public static function start() {
      // collect highly dependale environmental variables
      $var = array();
      $var[] = $_SERVER['REMOTE_ADDR'];
      $var[] = $_SERVER['HTTP_USER_AGENT'];
      // limit session validity until midnight
      $var[] = date('d.m.Y');
      // calculate hash
      $hash = sha1(implode('+',$var));
      // set session name from calculated hash
      session_id('MySessId'.substr($hash, 0, 30));
      // start session
      session_start();
   }
   public static function logout() {
      session_unset();
      session_destroy();
      self::start();
   }
}

class Security {
   public static function formToken() {
      // set the token      
      if(!isset($_SESSION['token']) || empty($_SESSION['token']))
         $_SESSION['token'] = sha1(uniqid().time());
      
      // if form was sent
      if(isset($_REQUEST) && !empty($_REQUEST)) {
         // if token is NOT the right token
         if(!isset($_REQUEST['token']) || empty($_REQUEST['token']) || $_REQUEST['token'] != $_SESSION['token'])
            die('Wrong token.');
         // if token is the right token
         if(isset($_REQUEST['token']) && $_REQUEST['token'] == $_SESSION['token']) {
            // process form here
            print_r($_REQUEST);
            $_SESSION['token'] = sha1(uniqid().time());
         }
      }
   }
}
Session::start();

Security::formToken();

if(isset($_POST['logout']) && $_POST['logout'] == true)
   Session::logout();
if(!isset($_SESSION['mail']) && isset($_POST['email'])) $_SESSION['mail'] = $_POST['email'];
?>
<!DOCTYPE HTML>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" /></head>
<body>
<form method="POST" action="/classes/index2.php">
<input type="text" name="email" />
<input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>" />
<input type="submit" />
</form>

<form method="POST" action="/classes/index2.php">
<input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>" />
<input type="hidden" name="logout" value="true" />
<input type="submit" value="Logout" />
</form>
<?php
echo (isset($_SESSION['mail'])) ? '<p>Your email adress "' . $_SESSION['mail'] . '" has been added to our database.</p>' : '';
?>
</body>
</html>
User avatar
jacek
Site Admin
Posts: 3262
Joined: Thu May 05, 2011 1:45 pm
Location: UK
Contact:

Re: Secure sessions

Post by jacek »

conradk wrote:No errors. No DB is used. I'm getting crazy.
If there are no errors, I have no idea.

I suggest taking a break from it until you get a reply form the hosting support guys ;)
Image
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Re: Secure sessions

Post by conradk »

They gave me a fix. I'll try it out and see ^^

EDIT: Their fix was BS. Not working xD How the hell can their be no error and behave differently on two servers ? I'm going to bed. I'll figure it out tomorrow. And I'm 100% gonna tell them to upgrade their server to the new version of PHP :lol:
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Re: Secure sessions

Post by conradk »

Unfortunately, we are not able to advise on what is causing the script to malfunction. If might be due to the different version of PHP you are running. However, at this time, it is not possible to upgrade the PHP version on our servers. When the upgrade has been implemented, we will notify our customers.

If you require additional assistance or information, please do not hesitate to write back.

Best regards,
Support
I think I'm changing to a small Kimsufi dedicated server soon so I can manage my server and only blame myself for bugs. That is the kind of response that just sucks IMO. When you actually need support, that's when they can't help you ... damn :/
User avatar
jacek
Site Admin
Posts: 3262
Joined: Thu May 05, 2011 1:45 pm
Location: UK
Contact:

Re: Secure sessions

Post by jacek »

conradk wrote:EDIT: Their fix was BS. Not working xD How the hell can their be no error and behave differently on two servers ? I'm going to bed. I'll figure it out tomorrow. And I'm 100% gonna tell them to upgrade their server to the new version of PHP :lol:
Well there would normally be an error if it was down to php versions being different. you could try var_dump() on the result of the function calls. One thing, $_REQUEST may not be available so you could try checking that by going to yourscript.php?thing=1 and seeing if thing => 1 shows up in $_REQUEST.
conradk wrote:I think I'm changing to a small Kimsufi dedicated server soon so I can manage my server and only blame myself for bugs.
Good idea :) You could also make sure that you had the most recent versions too. I have no idea why hosts do not update :?
Image
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Re: Secure sessions

Post by conradk »

jacek wrote:
conradk wrote:I think I'm changing to a small Kimsufi dedicated server soon so I can manage my server and only blame myself for bugs.
Good idea :) You could also make sure that you had the most recent versions too. I have no idea why hosts do not update :?
Yeh. Weird. At least on a ded server I could only blame myself. And I would get bandwidth without limits :lol: haha

My host says they will update once they have tested if PHP3.5 is compatible with their servers. Wtf ?

And they will only change once they have checked that php 5.3 will not affect customers negatively... so basically never ? They have 100K customers. Are they gonna check every single script ? Come on...
User avatar
jacek
Site Admin
Posts: 3262
Joined: Thu May 05, 2011 1:45 pm
Location: UK
Contact:

Re: Secure sessions

Post by jacek »

They should just go for it, the problem is that there are lot of terribly written scripts that get used and the people that use them will get annoyed when they stop working for no reason.

But yeah, they won't check all of the scripts. Most likely they will just wait for a while, it wasn't that long ago that most of them used php4
Image
conradk
Posts: 117
Joined: Tue Jul 05, 2011 10:41 pm

Re: Secure sessions

Post by conradk »

By changing $_REQUEST to $_POST the script partly works on my hosting account. I guess PHP 3.2.17 doesn't handle $_REQUEST the same way as PHP 3.5, which is confirmed by php.net -> changes were made to $_REQUEST handling.

Anyways, I'll just do my things offline. Unlimited bandwidth :mrgreen:
User avatar
jacek
Site Admin
Posts: 3262
Joined: Thu May 05, 2011 1:45 pm
Location: UK
Contact:

Re: Secure sessions

Post by jacek »

Well is configurable, I think the default changed between versions.

It's nice to be able to configure the server exactly how you want, I would never go back to shared hosting !
Image
Post Reply