Effective, efficient, foolproof, secure session handling

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

mgelinas
Forum Newbie
Posts: 13
Joined: Tue Jun 10, 2003 6:33 am
Location: QC Canada

Effective, efficient, foolproof, secure session handling

Post by mgelinas »

Consider this your guide for reliable session handling procedures using PHP 4.2.0 or later versions.

These procedures are effective, i.e, they work. I tested them in an autonomous EasyPHP development environment.

They are efficient, i.e., they are achieved with minimum code that is clean and easy to understand and adapt. The URL remains clean and short, and leads itself to be saved as a bookmark.

They are foolproof. They don't use or impose cookies that users may be suspicious of (with good reasons, nowadays), resent as an invasion of privacy or consider improper use of their hardware, and that malicious minds may tamper with. Users may switch computers and delete cookies with no adverse impact on session handling. Procedures don't rely on the GET method that exposes variables and values for all to know and that could be saved as an improper bookmark. They rather implement the POST method only as a vehicle for user input.

They are secure. They provide minimal hooks for crooked minds. No cookies to steal and tamper with. No revealing URLs.

First use PHP 4.2.0 and set a proper environment. The following procedures were not tested with prior or later versions. Test at your own risks on other versions, but certainly not prior to version 4.

Disable automated session parameters. Set session.auto_start to "off", set session.use_trans_id to "0" (zero) and set register_globals to "off" (as recommended by PHP authors). Take control of your session handling and minimize security risks.

Disable cookies. Set session.use_cookies to "off" and set variables_order to "EGPS". I leave the G (for GET method) to keep the EasyPHP "infos php" script running. In a web production environment, I would consider discarding it.

For testing purposes, you may want to set session.save_handlers to "files" to minimize other sources of trouble.

In example code below, the ellipsis dots indicate that some code precedes or that more code will follow.

Implement session handling function in your script as follows:

Code: Select all

<?php
session_start();
Catch the session identifier as a variable for use as an hidden form parameter to reconnect to session, as follows:

Code: Select all

$PHPSESSID = session_id();
Session variables are stored in the superglobal $_SESSION array. If your variable is not registered, setting it will register it. Yes, you can forget the deprecated session_is_registered and session_register functions.

Code: Select all

if (!isset($_SESSION['firstvar']))
  $_SESSION['firstvar'] = 'undefined'; // registering the variable a default of your choice
To adjust the session variable to a value input through a form, check for that value and set the session variable:

Code: Select all

if (!empty($_POST['firstvar']))
  $_SESSION['firstvar'] = $_POST['firstvar']; // setting the variable to form input
You can use the value in your session variable by calling it with $_SESSION['firstvar']. You can also call it with $firstvar if you fist create a variable alias (using the equal sign followed by an ampersand as the alias operator) as follows:

Code: Select all

$firstvar =& $_SESSION['firstvar'];
Let's recapitulate the code into a full script named step_one.php:

Code: Select all

<?php
session_start();
$PHPSESSID = session_id(); // for use in hidden form parameter to reconnect to session

if (!isset($_SESSION['firstvar']))
  $_SESSION['firstvar'] = '0'; // registering the variable a default of your choice
if (!empty($_POST['firstvar']))
  $_SESSION['firstvar'] = $_POST['firstvar']; // setting the variable to form input
$firstvar =& $_SESSION['firstvar'];

?>
<html>
<head>
<title>PHP Session Handling - Step One</title>
</head>
<body>
<h1>PHP Session Handling - Step One</h1>
<p>My firstvar is <?= $_SESSION['firstvar'] ?>. Yes, it is <?= $firstvar ?>.</p>
</body>
</html>
To test that session variables are carried over, change the above html code portion for the following:

Code: Select all

<html>
<head>
<title>PHP Session Handling - Step One</title>
</head>
<body>
<h1>PHP Session Handling - Step One</h1>
<p>My firstvar is <?= $_SESSION['firstvar'] ?>. Yes, it is <?= $firstvar ?>.</p>
<form action="step_two.php" method="POST">
<input type="hidden" name="PHPSESSID" value="<?= $PHPSESSID ?>">
<p>Select a number:
<select name="firstvar">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</p>
<input type="submit" name="submit" value="Submit">
</form>
</body>
</html>
The form in the step_one.php script calls upon the step_two.php script. Here it is:

Code: Select all

<?php
session_start();
$PHPSESSID = session_id(); // for use in hidden form parameter to reconnect to session

if (!isset($_SESSION['firstvar']))
  $_SESSION['firstvar'] = '0'; // registering the variable a default of your choice
if (!empty($_POST['firstvar']))
  $_SESSION['firstvar'] = $_POST['firstvar']; // setting the variable to form input
$firstvar =& $_SESSION['firstvar'];

if (!isset($_SESSION['secondvar']))
  $_SESSION['secondvar'] = 'X'; // registering the variable a default of your choice
if (!empty($_POST['secondvar']))
  $_SESSION['secondvar'] = $_POST['secondvar']; // setting the variable to form input
$secondvar =& $_SESSION['secondvar'];
?>
<html>
<head>
<title>PHP Session Handling - Step Two</title>
</head>
<body>
<h1>PHP Session Handling - Step Two</h1>
<p>My firstvar is <?= $_SESSION['firstvar'] ?>. Yes, it is <?= $firstvar ?>.</p>
<p>My secondvar is <?= $_SESSION['secondvar'] ?>. Yes, it is <?= $secondvar ?>.</p>
<form action="step_three.php" method="POST">
<input type="hidden" name="PHPSESSID" value="<?= $PHPSESSID ?>">
<p>Select a letter:
<select name="secondvar">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
</select>
</p>
<input type="submit" name="submit" value="Submit">
</form>
</body>
</html>
The new form in the step_two.php script calls upon the step_three.php script. Here it is:

Code: Select all

<?php
session_start();
$PHPSESSID = session_id(); // for use in hidden form parameter to reconnect to session

if (!isset($_SESSION['firstvar']))
  $_SESSION['firstvar'] = '0'; // registering the variable a default of your choice
if (!empty($_POST['firstvar']))
  $_SESSION['firstvar'] = $_POST['firstvar']; // setting the variable to form input
$firstvar =& $_SESSION['firstvar'];

if (!isset($_SESSION['secondvar']))
  $_SESSION['secondvar'] = 'X'; // registering the variable a default of your choice
if (!empty($_POST['secondvar']))
  $_SESSION['secondvar'] = $_POST['secondvar']; // setting the variable to form input
$secondvar =& $_SESSION['secondvar'];
?>
<html>
<head>
<title>PHP Session Handling - Step Three</title>
</head>
<body>
<h1>PHP Session Handling - Step Three</h1>
<p>My firstvar is <?= $_SESSION['firstvar'] ?>. Yes, it is <?= $firstvar ?>.</p>
<p>My secondvar is <?= $_SESSION['secondvar'] ?>. Yes, it is <?= $secondvar ?>.</p>
<p>If you successfully implemented these session handling procedures, let the contributor know by sending an email to 
<a href="mailto:PHPsession@laurentia.com?Subject=Comments on PHP session handling scripts">PHPsession@laurentia.com</a>.
 If you have improvements or any other comments, do the same.
</body>
</html>
The above scripts were tested and work. Happy PHP sessionning!

Contributed by Michel Gélinas, Canada. Email: PHPsession@laurentia.com

[Admin Edit: PHP tags added to make the code more readable]
User avatar
mikusan
Forum Contributor
Posts: 247
Joined: Thu May 01, 2003 1:48 pm

Post by mikusan »

Many people do not have access to php.ini, me, and many don't want to write all that code into every php script, me. I would rather suggest what mgelinas said but using your own session handling, that is write your session handler and use a database, that way you have full control and php.ini's presets are overridden. That way you can patch security holes by making multiple checks on the IP and referrer, you can control basically every aspect of sessions even garbage collect. The only catch is that writing your own session handler can be tedious and sometimes quite tough if you are at your first arms, but well i went through all of that and although mine is not 100% secure i have full control over it, AND it's in my common library so whenever i need it all i do is say mysessionhandler(); and it does it all... :)

Also i am really unsure about hidden fields, because last time i checked they can be seen in the html code if you check the source, why would you want to pass session variables through hidden fields??
User avatar
twigletmac
Her Royal Site Adminness
Posts: 5371
Joined: Tue Apr 23, 2002 2:21 am
Location: Essex, UK

Post by twigletmac »

Hi, mgelinas would it be possible for you to edit your post and put the code into [syntax=php][/syntax] tags for readability?

Mac
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Disabling cookies and transpartent session propagation doesn't really increase your security. Cookies actually give you a much greater degree of control over the authenticator than hidden form fields do.

From a useability perspective, if you've gotten rid of cookies AND GET style URL session id propagation, you're required to have all in-house links be button-forms, which is a no-no....
[]InTeR[]
Forum Regular
Posts: 416
Joined: Thu Apr 24, 2003 6:51 am
Location: The Netherlands

Post by []InTeR[] »

twigletmac wrote:Hi, mgelinas would it be possible for you to edit your post and put the code into tags for readability?

Mac
Can't u do that :), where is the power of the admin go'ing to..... :D :D :D
User avatar
twigletmac
Her Royal Site Adminness
Posts: 5371
Joined: Tue Apr 23, 2002 2:21 am
Location: Essex, UK

Post by twigletmac »

[]InTeR[] wrote:Can't u do that :), where is the power of the admin go'ing to..... :D :D :D
I know I can do it but I like to make the people not doing it aware of how they can.

Mac
User avatar
liljester
Forum Contributor
Posts: 400
Joined: Tue May 20, 2003 4:49 pm

Post by liljester »

how is sending your session id via a hidden form element more secure than a cookie ? the session info is still encoded in the post data of the header isnt it?

granted, i would have to see the source of the page to get the session id, but if i did get it i could write my own script to post stuff to your session from my webserver. i think =)
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Exactly, in either case cookie or form, if you're not MAC'ing the data your data source is suspect
Last edited by nielsene on Tue Jun 10, 2003 3:42 pm, edited 1 time in total.
User avatar
mikusan
Forum Contributor
Posts: 247
Joined: Thu May 01, 2003 1:48 pm

Post by mikusan »

It's not, totally not. but first why would someone want to pass the session ID? that's just funny, unless you close a window the session id is not destroyed...and a hidden field here will not keep it alive... IF you are the cookies and milk kinda person cookies would b the perfect way to store a session ID for future use...but why? In my case when i register a session i give it an expiry, if the person does not reactivate/use the session the expiry is not updated and after sometime the garbage collect comes along and takes it all away. IMHO hidden fields and the methods suggested above are not a great method and people should not use it unless they have been programming already and need to cut the rules for once in a while, security wise this is almost suicide but oh well... i have one but i suggest that people write their session handling by overriding the standard session handler, this is an example from the BIBLE, but if you need help adapting it to the database i or many people out there i am sure can help you...

Code: Select all

<?php
function open ($save_path, $session_name) {
  global $sess_save_path, $sess_session_name;
       
  $sess_save_path = $save_path;
  $sess_session_name = $session_name;
  return(true);
}

function close() {
  return(true);
}

function read ($id) {
  global $sess_save_path, $sess_session_name;

  $sess_file = "$sess_save_path/sess_$id";
  if ($fp = @fopen($sess_file, "r")) {
    $sess_data = fread($fp, filesize($sess_file));
    return($sess_data);
  } else {
    return(""); // Must return "" here.
  }

}

function write ($id, $sess_data) {
  global $sess_save_path, $sess_session_name;

  $sess_file = "$sess_save_path/sess_$id";
  if ($fp = @fopen($sess_file, "w")) {
    return(fwrite($fp, $sess_data));
  } else {
    return(false);
  }

}

function destroy ($id) {
  global $sess_save_path, $sess_session_name;
       
  $sess_file = "$sess_save_path/sess_$id";
  return(@unlink($sess_file));
}

/*********************************************
 * WARNING - You will need to implement some *
 * sort of garbage collection routine here.  *
 *********************************************/
function gc ($maxlifetime) {
  return true;
}

session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");

session_start();

// proceed to use sessions normally

?>
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

mikusan wrote:It's not, totally not. but first why would someone want to pass the session ID? that's just funny, unless you close a window the session id is not destroyed...and a hidden field here will not keep it alive... IF you are the cookies and milk kinda person cookies would b the perfect way to store a session ID for future use...but why? In my case when i register a session i give it an expiry, if the person does not reactivate/use the session the expiry is not updated and after sometime the garbage collect comes along and takes it all away. IMHO hidden fields and the methods suggested above are not a great method and people should not use it unless they have been programming already and need to cut the rules for once in a while, security wise this is almost suicide but oh well... i have one but i suggest that people write their session handling by overriding the standard session handler, this is an example from the BIBLE, but if you need help adapting it to the database i or many people out there i am sure can help you...

[snipped out database handler]
Why would someone want to pass the session ID? There a lots of reasons for wanting to propagate a session ID even if forms are not being used. Lets say you're doing a CMS type site, once you've logged in you should stay logged in, and the site should be able to use regular hrefs for its navigation to major areas. Hence you have to form to use. Therefore you have to allow either cookies of GET URL propagations. You can set expiration times on a cookie and if you protect it with the MAC you can still detect tampering there.

You later say, "security wise this is almost suicidal", but you listed this as "foolproof and secure" in the thread title, so you shouldn't take offense when we call you on it. Likewise you say " IMHO hidden fields and the methods suggested above are not a great method and people should not use it unless they have been programming already and need to cut the rules for once in a while". I don't see how that warrants the title "Effective, efficient, foolproof, secure session handling".
User avatar
mikusan
Forum Contributor
Posts: 247
Joined: Thu May 01, 2003 1:48 pm

Post by mikusan »

[quote="nielsene]Once you've logged in you should stay logged in, and the site should be able to use regular hrefs for its navigation to major areas. [/quote]

On that i agree, but then why not just authenticate the user that is using that current session by using a custom sessid that includes the user's IP, that is you can keep using that session for a delimited amount of time $expiry and you have to be using the same IP. If someone wants to stay logged in for a year, and is using a modem, then we use cookies, i still don't think i would ever use hidden fields.

Also, perhaps i am not understanding this clearly, please correct me i am no Guru and not even imaginably close to it, but if you are using multiple forms then there should be no problem in using the same session from form to form, i did that... But then again perhaps i am missing something...
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

First let me apologize to mikusan, in my last post I confused him with the OP.

However, I think mikusan is confusing how sessions work. Sessions pass the session ID from page to page using one of two means. The first, and default, is via a PHP generated cookie. You don't have to issue the set_cookie call yourself. PHP takes care of it behind the scenes. If the user refused cookies, or you turn off cookie based sessions as the OP suggested, then PHP falls back to using appending phpsessid=<long hash value> to all relative URLs, adding it is a hidden form element, etc in order to propagate it. The OP suggested turning off cookie based sessions, and turning off transparent propagation. This means that PHP will not attempt to propagate the session ID, in ANY MANNER.

Now this was one of the reasons why the OP said it was more secure and foolproof. However it will break navigation and the user experience, unless you as the developer do in and do all the things PHP handles for you automatically.

I would suggest the better option is use the regular PHP session methods, but add some session variables to protect the session ID from hijcaking.
mgelinas
Forum Newbie
Posts: 13
Joined: Tue Jun 10, 2003 6:33 am
Location: QC Canada

Post by mgelinas »

Well, I guess I am the OP (if OP stands for original poster).

There are so many misleading or incomplete tutorials on how to implement sessions that work and I lost so much time over the last few months, I thought this could be helpul to let others know to make sessions work. Don't focus too much on the secure aspect. Focus on effective, efficient and foolproof. I am far from a security guru. For one, security gurus don't master the art of clear meaningful speech.

Yes, disabling cookies and GET imposes provision of form buttons to navigate the site. This is what I intend to do on the site I am developing. I will bother to do this even if some say this is a no-no. (Why do they say so? Doesn't HTML allow for different and creative ways of doing things?)

There are problems with cookies being disabled by the user, borrowed (as easily as copying a file in Windows), hijacked or tampered with as they are simple text files. If you make cookie contents unreadable, then users get worried about what they do and possibly shouldn't. If users switch computers (like using someone's else for a quick information check), the cookie is not there or is the wrong cookie anyway. As for me, cookies are outdated technology.

If you hide the session ID, it is much a much better user experience than showing it as a long appendice to the short URL you carefully designed. Users don't need to see the 32 gooblygook characters.

I was not dealing with user authentication. I was offering a way to make it a bit tougher on hijackers. Like a lock on a door. Sure it can be broken. But it works so well that most of us entrust the safety of our homes with such devices. Yes, burglars can still come in. Most others would not break and enter. This is a level of safety that works. A home with a lock on doors is a safe home. Ask Stanley.

mgelinas
User avatar
mikusan
Forum Contributor
Posts: 247
Joined: Thu May 01, 2003 1:48 pm

Post by mikusan »

Now i see what you are saying thanks you cleared some things out for me too, but let me ask you <b>nielsene</b> i wrote my own session handler so that i can use some tricks and so that i can use a database to store the data, when i did that i was sure i have completely overridden the default session handler, that includes session cookies, as you said though that is not the case, then may i ask you, (assuming that i cannot see and or edit php.ini which i can't) then i don't exactly have a cookie sent to my computer when i log in to my site, and i don't have a superlong URL including the php sessid. Then how are my sessions recognized and passed from page to page... how can i override the current cookie session settings?
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

mgelinas wrote:Well, I guess I am the OP (if OP stands for original poster).
Yup OP=Original Poster
There are so many misleading or incomplete tutorials on how to implement sessions that work and I lost so much time over the last few months, I thought this could be helpul to let others know to make sessions work. Don't focus too much on the secure aspect. Focus on effective, efficient and foolproof. I am far from a security guru. For one, security gurus don't master the art of clear meaningful speech.
Well Jason has several good session tutorials on his site, as well as some of the stickies.

On the security front, I'll link one of my earlier threads on howto secure a cookie, which includes building tamper resistant sessions.

[Mod's If I'm linking to old material of mine too often, please let me know.]
Yes, disabling cookies and GET imposes provision of form buttons to navigate the site. This is what I intend to do on the site I am developing. I will bother to do this even if some say this is a no-no. (Why do they say so? Doesn't HTML allow for different and creative ways of doing things?)
WellI would argue that its not an effective or foolproof solution. I can think of a lot of users who would read your column and then get caught. Using your solution imposes a very large burden on the site architect. Plus its a form of security through obscurity which is never a good primary tool.
There are problems with cookies being disabled by the user, borrowed (as easily as copying a file in Windows), hijacked or tampered with as they are simple text files. If you make cookie contents unreadable, then users get worried about what they do and possibly shouldn't. If users switch computers (like using someone's else for a quick information check), the cookie is not there or is the wrong cookie anyway. As for me, cookies are outdated technology.
Seethe linked article for dealing with the "borrowing" or editting aspect. Your code does
not deal with users switching computers either. If users are disabling cookies, then yes cookies won't work. But the underlying GET method would still work if using native sessions. Cookies are not outdated and won't be until a stateful HTTP-like protocol is embraced en mass.
If you hide the session ID, it is much a much better user experience than showing it as a long appendice to the short URL you carefully designed. Users don't need to see the 32 gooblygook characters.
Hiding the session ID from the user is a better user experience, granted. Hiding it in a cookie and being able to use regular URLs is even better than using only buttons.
Post Reply