SitePoint Sponsor |
|
User Tag List
Results 1 to 19 of 19
Thread: User Sessions
-
Jun 1, 2007, 17:20 #1
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
User Sessions
OK now that I have finally gotten opinions from many on how to hash and protect users passwords I am ready to stir up discussion about users sessions. I am still pretty new to this and the one thing I know for sure is not to store a users password in a session. The biggest thing to me is validating the user with the sessions because I have been told on a shared server other users can tamper with these sessions. So feel free to give me your input on this topic.
Kayzio - We don't hesitate, we accelerate.
-
Jun 1, 2007, 17:35 #2
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Google: session hijacking
Actually, I'm sure a search on this forum will turn up a lot about it. There's been many methods suggested for avoiding it, including storing the User Agent string in a session and/or storing the user's IP (or subnet).
As for making sure other users on the server don't tamper with your sessions, I don't think there's anything you can do about that. Do as much verification as you reasonably can on session data, but you can't really stop them in a shared hosting environment.
Actually, one method I saw once was as follows:
Concatenate every session variable value into one long string, hash it with a secret key, and store that in your session (when concatenating session variables, don't include this hash). Do this at the end of every script (to catch any/all updates you make during the script), and at the beginning of every script verify it. This gives you a nearly-foolproof method for catching any tampering of your session data.
Code php:$secretkey = "Some secret key hard-coded into your scripts"; $sess_str = ""; foreach($_SESSION as $key=>$val) { if($key == "checksum") continue; //don't include the hash $sess_str .= $val; } $hash = sha1($sess_str.$secretkey); if($hash != $_SESSION['checksum']) { //session tampering! abort! terminate! }
All that said, I don't think this is worth the effort. It's going to slow down your scripts, and to be perfectly frank I don't think you need to worry about other users in your shared hosting environment tampering with your sessions. It's just not worth it.
If you are worried about someone tampering with your sessions, then write your own session handling method that stores everything in your database. That way there are no files in the temp directory for anyone to mess with. This is the method I use, although I chose to do so a) for the experience and b) so that I could easily scale up to a giant, load-balanced server farm (albeit a very unlikely happenstance). My database-based sessions are at least as fast as PHP's file-based session (database reads are often faster than file system accesses, and unless the database is poorly designed are almost never slower), and they are more secure because no one can get to my temp files because I don't have any (although that's not a concern in my case since I'm on a virtual dedicated server).
-
Jun 1, 2007, 17:49 #3
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
how exactly do you go about database sessions? for example when a page loads how do I check the database to see if they are logged in? Do I have to perform a query that searches for there ip in the database or something? I have thought about learning to do database sessions but never found any good in depth tutorials on it.
Kayzio - We don't hesitate, we accelerate.
-
Jun 1, 2007, 17:56 #4
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Never mind I finally came across one that looks good: http://www.devshed.com/c/a/PHP/Stori...in-a-Database/
Kayzio - We don't hesitate, we accelerate.
-
Jun 1, 2007, 18:03 #5
This is what I do, maybe I could get some feedback on my method.
1) Store their session id and a hashed flag consisting of their USER_AGENT and a simple salt.
2) if a session id is set, ie logged in, then at the top of every pageload - regenerate_session_id() so that everytime the user does something - they are given a new ID.
3) on pages that require login, make sure that they have a session ID, and the session flag matches the server user_agent, which should since a user's ip may change but they can't use different browsers with the same session. if its different, kill the whole session.
-
Jun 1, 2007, 18:05 #6
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
That's one way to do it. I built my session handler from scratch. Essentially I created a new database table, sessions, that does little more than store a session ID and link that to a user ID in the users table. Each time a page loads, I check if the session cookie exists, and if it does I look up the ID stored in it in my session table; if I find it, I load up that user's data into my user object. If the cookie doesn't exist or the session isn't in my table, the user isn't logged in and I redirect them to the login page.
I said "little more"; I also store a session expiration time so that I know when a session expires. Additionally, I store session variables in name=value pairs delineated by the '&' character (just like GET parameters in a URL); PHP has a handy function called parse_str for extrapolating data stored like that. Thus I have all the flexibility of PHP's sessions with the power of being fully database-based. And I have a deep understanding of what exactly sessions are and how exactly they work, more so than someone who merely reads just enough to know how to use $_SESSION.
-
Jun 1, 2007, 18:07 #7
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I think regenerating the session ID on every page load is perhaps a bit excessive, but if it's working for you then don't let me stop you. I'd do a little more than merely check for a session ID to verify that a user is logged in (like verify your hashed user agent string), but I'm sure you do do that and you merely neglected to mention so.
Edit:
Oh, you did mention that you do more to validate... I should have read your post more closely. I apologize.
-
Jun 1, 2007, 18:10 #8
#3 the session flag matches the server user_agent
maybe i worded it terribly, i don't blame you for not understanding me but thats what it said.
I only regenerate logged in users - which would alleviate as much load as possible since its an auction site and many will browse through without actually logging in.
-
Jun 1, 2007, 18:13 #9
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
One more note on regenerating session ID every time - I could be mistaken, but I believe PHP generates a new session file in your temp directory each time a new session ID is generated. Thus, a user who browses across 20 pages will have 20 files in your temp directory, since PHP only cleans up the directory every so often and only when such a file has expired (as per session.expire or whatever it's called). So this would be a very bad idea for a high-traffic site because you'd fill up your entire disk with these orphaned sessions. Should be fine for a low-traffic site, however, except that the increased file system access could slow you down.
-
Jun 1, 2007, 18:17 #10
-
Jun 1, 2007, 18:36 #11
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Yea I plan to go strictly database driven with sessions and in the article I found it talks about how database sessions are a lot more friendly when it comes to large sites running on multiple servers.
Kayzio - We don't hesitate, we accelerate.
-
Jun 1, 2007, 19:27 #12
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Here is what I have so far:
PHP Code:<?php
class SessionManager {
/**
* When True Error Reporting Is Enabled
*
* @public bool
*/
public $ErrorReporting = false;
/**
* Session Life Time
*
* @public bool
*/
public $LifeTime;
/**
* Close
*/
function Close() {
return true;
}
/**
* Destroy
*
* @param int
*/
function Destroy($ID) {
$NewID = mysql_real_escape_string($ID);
$SessionData = $DB->Query("DELETE FROM sessions WHERE id = '$NewID'");
return true;
}
/**
* Garbage Collection
*/
function GarbageCollection() {
$SessionData = $DB->Query("DELETE FROM sessions WHERE expires < time()");
return true;
}
/**
* Session Manager
*/
function Manager() {
$this->LifeTime = get_cfg_var("session.gc_maxlifetime");
session_set_save_handler(
array(&$this, "Open"),
array(&$this, "Close"),
array(&$this, "Read"),
array(&$this, "Write"),
array(&$this, "Destroy"),
array(&$this, "GarbageCollection")
);
}
/**
* Open
*
* @params string, string
*/
function Open($SavePath, $SessionName) {
global $sess_save_path;
$sess_save_path = $SavePath;
return true;
}
/**
* Read
*
* @param int
*/
function Read($ID) {
$Data = "";
$Time = time();
$NewID = mysql_real_escape_string($ID);
$SessionData = $DB->Query("SELECT data FROM sessions WHERE id = '$NewID' AND expires > $Time");
$NumRows = $DB->NumRows($SessionData);
if ($NumRows > 0) {
$Row = $DB->FetchAssoc($SessionData);
$Data = $Row['data'];
}
return $Data;
}
/**
* Write
*
* @params int, string
*/
function Write($ID, $Data) {
$Time = time() + $this->LifeTime;
$NewID = mysql_real_escape_string($ID);
$NewData = mysql_real_escape_string($Data);
$ReplaceSession = $DB->Query("REPLACE sessions (id, data, expires) VALUES ($NewID, $NewData, $Time)");
return true;
}
}
?>Kayzio - We don't hesitate, we accelerate.
-
Jun 5, 2007, 10:40 #13
-
Jun 5, 2007, 15:14 #14
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Thanks for the tip kromey.
Kayzio - We don't hesitate, we accelerate.
-
Jun 5, 2007, 15:24 #15
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Generating a new session id on each page load is a bad idea. It effectively prevents the user from having multiple windows open to the same site (using tabbed browsing for example). It's not needed either. To prevent session-hijacking, you just need to regenerate id when users privileges changes (eg. when a user logs in).
-
Jun 5, 2007, 15:33 #16
-
Jun 5, 2007, 16:32 #17
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
good point wonshikee, never looked at it like that. I am glad I didn't write out all of that code. Also any suggestions to improve what I have.
Kayzio - We don't hesitate, we accelerate.
-
Jun 5, 2007, 17:20 #18
- Join Date
- Sep 2006
- Location
- Fairbanks, AK
- Posts
- 1,621
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Two comments Kayzio:
The first is more of a nitpick - your comments indicate that $LifeTime is a bool when it is in fact an int.
The second is especially relevant given your desire to make this compatible with more than one database: REPLACE is a MySQL-specific extension of the SQL standard and thus will not work on other databases. I'd suggest rewriting that as an UPDATE statement (in your Write method).
-
Jun 5, 2007, 19:06 #19
- Join Date
- Mar 2007
- Posts
- 196
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
nice find lol and thanks for letting me know replace is mysql only i'll make sure i fix that
Kayzio - We don't hesitate, we accelerate.
Bookmarks