Sign in to follow this  

[web] PHP, sessions and security

This topic is 3866 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all, I'm writing my own forum software, and I'm having a few issues with sessions. I'm using PHP sessions for logging users in, and keeping them logged in. The way I do this is, when a user logs in I check username and password, then store their user ID in $_SESSION. On each page, I call session_start(), then check to see if $_SESSION['uid'] is set, and if so then I grab that user from the database and use them as the active user. My questions are: 1) Is this secure? It just seems a bit horrible for me to be storing the user ID and then assuming it's correct. Is there not any way a user can fake the $_SESSION variable and log in as another user? 2) This requires cookies to be enabled, correct? And if so, is it worthwhile implementing some method that doesn't require cookies? 3) I see PHPBB doesn't use PHP sessions, it does it all manually. Anyone know if there's a good reason for this? Cheers, Steve

Share this post


Link to post
Share on other sites
I'm pretty sure that PHP sessions just store a Session ID in the cookies and pulls the real session data from a database or flatfile of some sort. If someone were to get your Session ID and set it as their own, they might get access to the user secured information. If you're setting this up on a shared hosting environment, I believe session data is stored in /tmp and anyone else on the shared server could potentially get access to your session data. With access to that it would be fairly easy for someone to see what kind of information you're storing in the session and based on that, create or modify their own session on the server in order to log in to your system with false credentials.

As far as doing it without cookies, you'd have to create a hash of some sort based on their IP and browser info, both which are easy to spoof, or keep their 'session id' in the URL which is even easier to read then in a cookie.

All that said, I usually just use the $_SESSION variables.

Share this post


Link to post
Share on other sites
I use your simple $_SESSION['uid'] method, but also keep track of a few bits of information (their browser type and IP address hashed along with a password) and if this changes after they log on then their session is cleared as a crude defense against session hijacking. (Session hijacking is the term you might want to look up - there is a vast amount of information on this topic). IP addresses are not exactly bullet-proof (two people on a work network could share the same IP, or the remote IP might change depending on ISPs).

The only security issue I've had thus far was using a particular page you could view other user's details - and by looking at their details you'd end up logged in as that user. This, however, was simply because register_globals was enabled and setting the local variable $uid was also overwriting the value in $_SESSION['uid'].

Share this post


Link to post
Share on other sites
Awesome, that's all good to know, thanks. Any ideas about #3? One person told me "Because it's a bloated piece of s*it" [smile]

Share this post


Link to post
Share on other sites
Isn't PHPBB notorious for having security problems? I wouldn't base any decisions on what they have implemented.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Awesome, that's all good to know, thanks. Any ideas about #3? One person told me "Because it's a bloated piece of s*it" [smile]

Reasons phpBB might do it are:

  • PHP hasn't always had built in session support; maybe it's a hold-over from an earlier incarnation.

  • If the server is set up wrong, it can break the built in session support (for example, when I tested a PHP based website on the sourceforge project web servers, I found that session support was disabled/broken, so I had to write a session data handler to put all the session data into MySQL instead).

  • For servers that host several sites (ie, most of them), there will only be a single location used to store session data, and it will be readable and writable by the user under which PHP is running (or for slightly less well run web-hosts, it will be world readable/writable). This means that anyone else with hosting on that machine can write a script to access or edit that session data. That is of course a security hazard (although really, it's pretty hard to set up a server to have multiple virtual hosts and maintain security between them - insecure session data isn't really the biggest problem)

  • PHP can be configured to not use cookies to pass session IDs, or to try using cookies and fall back if they don't work. The alternative/fall-back is for PHP to scan the HTML that you're emitting, and modify any tags that contain links so that it can add the session ID to the query string. Personally, I've always found this to be a big pain in the butt, and very unreliable. You have some measure of control over it through the PHP configuration, but you just can't get the same amount of control that you'd get by doing it manually.


So the simple answer is: phpBB includes its own session handling code because PHP sessions are highly dependant on the server being configured correctly for the situation, and that just isn't something that can be relied upon for a piece of software designed to just start up and work correctly on almost any server.

John B

Share this post


Link to post
Share on other sites
Quote:
Original post by tstrimp
Isn't PHPBB notorious for having security problems? I wouldn't base any decisions on what they have implemented.
Good point [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by tstrimp
Isn't PHPBB notorious for having security problems? I wouldn't base any decisions on what they have implemented.


phpBB is mostly notorious for older versions being exploited, and vast masses of users not running the latest versions because the modified their forum's code (phpBB 2.x does not have a plugin system for modifications). Someone once wrote a worm that exploited some older versions. If you run phpBB you should upgrade when a new version is released. Collary: don't modify your forum's code (much).

Share this post


Link to post
Share on other sites
What you might want to do is store a login hash, that way you don't need to have a password in the session directly, but you can still verify credentials.

Share this post


Link to post
Share on other sites
Quote:
Original post by Pete Michaud
What you might want to do is store a login hash, that way you don't need to have a password in the session directly, but you can still verify credentials.

You should never store a password in the session.

What would be the point of that anyway? You only log in once, after that the newly created session can be identified using its session id (which in turn can be stored in the database together with other information like the user's ip address, to increase security).

Share this post


Link to post
Share on other sites
Since I'm commonly setting up web sites with a CubeCart installation for e-commerce (not all the time, but CubeCart gets the job done for 85% of anything you need to sell) I've incorporated the same session administration feature into the web site and the content management / backend of the web site as well - so if the need arises, logged in users to the web site will not have to re-register at the store section.

They handle it the same way as you, however they regenerate the session id at login.You also want to look into using mysql_real_escape to prevent sql injection attacks.

There's a lot more you can do to 'be on the safe side'. Give your sessions a timeout variable and update it each time they hit the next page and the timeout hasn't occured.

http://phpsec.org/projects/guide/4.html

http://www.devshed.com/c/a/PHP/Creating-a-Secure-PHP-Login-Script/

Share this post


Link to post
Share on other sites
Oh, use the md5 or sha1 function on the password before storing it in the database, then compare passwords by applying the function before the sql syntax. It computes the given password to a hash value. The point? You're database is leaked and they get a large list of hashes and usernames instead of usernames and passwords. It'll provide you the time to go through and change all the passwords quickly so that your customer's information is not leaked.

Share this post


Link to post
Share on other sites
Yeah, I've been storing SHA-1 hashes in my database, and I'm sure to escape strings properly before putting them anywhere near the DB.

I might do the session timeout, but I think that might be a bit annoying for some users. My forums don't need to be ultra-secure and I think it's unlikely that anyone will try session hijacking.

Share this post


Link to post
Share on other sites
Quote:
Oh, use the md5 or sha1 function on the password before storing it in the database


I recommend something tougher. MD5 hashes for (short) passwords are easily recoverable with the help of rainbow tables. Take a look at the hash functions to see what's available. MD5's good enough for e.g. a blog or a forum which are not interesting for a hacker to steal and run through rainbow tables, but you should push for the best when dealing with e-commerce or other sensitive (private) data.

Share this post


Link to post
Share on other sites
One thing about storing session ID's in the URI: What happens when a malicious user links to the site, and gets the ID using the referrer? Though, it'd be unlikely someone would go through the trouble to do this, and unlikely that they'd be monitoring the data gathered from potential victims.

I'm currently designing a CMS, Thacmus, and the user security is very basic - essentially what markadrake and others have stated: I just store the username and password (hashed with md5) in the session. However, looking at Sander's suggestion, I might change that some.
I can't say the security's really been tested in the real world, I've only released it recently, and there's been only a couple or so unique visitors to the website running the CMS.

I might be designing a forum using Thacmus later, but I don't think I'd worry about users with cookies disabled. Not every user base can be covered.

Share this post


Link to post
Share on other sites
Quote:
I just store the username and password (hashed with md5) in the session.


Listen to benryves and also store the user's IP address and user-agent string in the session table. When you get a session ID simply check that the first 3 parts of the IP address(*) and the user-agent string are still the same as what you stored in your session table. If not, silently destroy the session and create a new one.

(*) Matching only the first 3 parts, e.g. 192.169.1.* ensures that people using a broken ISP that reroutes traffic through different proxies on each request (e.g. AOL, some dial-in ISP's) can still match their sessions.

Share this post


Link to post
Share on other sites
I noticed everyone's replies hit all of my points except session regeneration at login.

Sander - thanks for the link. But it doesn't necessarily say which is strongest of the available types. It's also 3 am and I'm feeling the inevitable keyboard / face-smack that will occur soon.

Share this post


Link to post
Share on other sites
Ok, here is how you do a secure login without HTTPS:

0. Use session_set_save_handler() to implement secure session storage (maybe in a database) or session_save_path() to use another directory than /tmp.
1. In the login form, have a hidden field with some random value set by the server.
2. The client will use hex_md5(key) on the password and then hex_hmac_md5(hiddenkey, crypted_password) on the result and send it to the server.
3. The server uses the same algorithm to validate against the database of login users.
4. (Assuming validated) Set $_SESSION['login_user'] to the value of the logged in user and don't care anymore. Use session_regenerate_id() to invalidate the session id.
5. ...
5. Profit! =)

Share this post


Link to post
Share on other sites
Quote:
Original post by Sander
Quote:
Oh, use the md5 or sha1 function on the password before storing it in the database


I recommend something tougher. MD5 hashes for (short) passwords are easily recoverable with the help of rainbow tables. Take a look at the hash functions to see what's available. MD5's good enough for e.g. a blog or a forum which are not interesting for a hacker to steal and run through rainbow tables, but you should push for the best when dealing with e-commerce or other sensitive (private) data.


Unsalted md5 can be recovered quickly through rainbowtables, usually within a few minutes. (maybe hours)

However, store a salt value, and use it.

ie. caluclate sha1(salt ^ md5(password)) with a 128 bit salt. You store the salt as an additional field.

This is surprisingly secure against attack. (brute force/dictionary attacks are the ony things that can put a dent in, however any precomputed attack (time/memory tradeoffs like rainbowtables, ktables, etc.) are foiled.)

Also, you could create a derived key from the password and username (ie. sha1(md5(username) ^ md5(password)))), and encrypt your sensitive data with that when needed. (if you have anything that sensitive).

Share this post


Link to post
Share on other sites
Im just beginning with PHP so this topic is pretty interesting to me. Forgive me if this is naive, but what is wrong with the following security model:


1) On the login page, gather their username and password.
2) Compare these with the USERPASS table in the database.
3) If the login is valid, generate a Big Random String (BRS) and store it in the SESSION table in the database. Also return the BRS to the login page.
4) For every secure page on your site, check that the BRS (passed on GET or POST) is in the SESSION table. If it is, you know its a valid user.
5) When the user logs out, or times out or whatever, remove the BRS from the SESSION table.

This doesnt use Cookies, or the Session PHP utility, but It does of course use mySQL.

Comments?

Share this post


Link to post
Share on other sites
Quote:
Original post by AndreTheGiant
Im just beginning with PHP so this topic is pretty interesting to me. Forgive me if this is naive, but what is wrong with the following security model:


1) On the login page, gather their username and password.
2) Compare these with the USERPASS table in the database.
3) If the login is valid, generate a Big Random String (BRS) and store it in the SESSION table in the database. Also return the BRS to the login page.
4) For every secure page on your site, check that the BRS (passed on GET or POST) is in the SESSION table. If it is, you know its a valid user.
5) When the user logs out, or times out or whatever, remove the BRS from the SESSION table.

This doesnt use Cookies, or the Session PHP utility, but It does of course use mySQL.

Comments?


You just described how PHP sessions work when cookies aren't enabled in the browser :D
... with some minor differences: sessions are stored in a file on disk and not a db table and php takes care of rewriting the links to include that BRS (your BRS is called "session id" actually).

Also, there are several (minor) problems with the approach:
1. passing around session id ("brs") via get: depends on "how big your brs really is"
2. some user can copy+paste a link (which includes the "brs" if you pass around via get) and send it to someone and then that second person would be automatically loggged-in via the first users' account
3. BIG performance issues with storing session in mysql! ok, on a personal website with 50 hits/day is no problem, but if your site has lots of visitor, every page view means a select AND an update to the same table. And the wonderfull mysql uses table-locks (at least with MyISAM) so the site will be slow and painfull.

basically, there's nothing 'badly wrong' with the model you described... that's why php uses same model (or almost the same).. but I would recommend trusting the php session code (you can write your own session handlers and still let php taking care of cleaning old sessions and generating sessions id's), it has been tested intesively by lots of sites :)

Share this post


Link to post
Share on other sites
Quote:
Original post by cobru
Quote:
Original post by AndreTheGiant
Im just beginning with PHP so this topic is pretty interesting to me. Forgive me if this is naive, but what is wrong with the following security model:


1) On the login page, gather their username and password.
2) Compare these with the USERPASS table in the database.
3) If the login is valid, generate a Big Random String (BRS) and store it in the SESSION table in the database. Also return the BRS to the login page.
4) For every secure page on your site, check that the BRS (passed on GET or POST) is in the SESSION table. If it is, you know its a valid user.
5) When the user logs out, or times out or whatever, remove the BRS from the SESSION table.

This doesnt use Cookies, or the Session PHP utility, but It does of course use mySQL.

Comments?


You just described how PHP sessions work when cookies aren't enabled in the browser :D
... with some minor differences: sessions are stored in a file on disk and not a db table and php takes care of rewriting the links to include that BRS (your BRS is called "session id" actually).

Also, there are several (minor) problems with the approach:
1. passing around session id ("brs") via get: depends on "how big your brs really is"
2. some user can copy+paste a link (which includes the "brs" if you pass around via get) and send it to someone and then that second person would be automatically loggged-in via the first users' account
3. BIG performance issues with storing session in mysql! ok, on a personal website with 50 hits/day is no problem, but if your site has lots of visitor, every page view means a select AND an update to the same table. And the wonderfull mysql uses table-locks (at least with MyISAM) so the site will be slow and painfull.

basically, there's nothing 'badly wrong' with the model you described... that's why php uses same model (or almost the same).. but I would recommend trusting the php session code (you can write your own session handlers and still let php taking care of cleaning old sessions and generating sessions id's), it has been tested intesively by lots of sites :)


1. I guess I would have to keep the BRS under 1024 characters, or whatever is supported on the GET string. Which is still more than enough to still be secure right? Either that or use the POST method which is probably what I would do anyway.

2. I dont care. If a user wants to let someone else log in to their account,they could just as easily give that person their password, as the BRS right? Am I missing something here? Also, using POST would get around this problem as well.

3. Did not know that. For the time being however I think Im safe ;)

Thanks a lot for the advice, and the quick response!
So basically I should use php's Session support since it basically does what I desribed, only is already written for me, and probably has some other nice features I havnt thougth of.

Thanks!

Share this post


Link to post
Share on other sites
Quote:
Original post by AndreTheGiant
2. I dont care. If a user wants to let someone else log in to their account,they could just as easily give that person their password, as the BRS right? Am I missing something here? Also, using POST would get around this problem as well.
The problem is that the user might not realise that part of the link allows someone to log in as them. Particularly if there are other parameters on the URL. And you can't expect users to have to cut out part of the URL when they copy the link, surely?

Share this post


Link to post
Share on other sites
If you can possibly get away with it, using cookies is a MUCH BETTER idea than putting session IDs in the query string.

PHP has an ini setting (which you can change dynamically too provided you do it before session_start) called "session.use_only_cookies".

I cannot stress how important this is for security. Allowing session IDs in the query string opens up your application to a whole host of attacks, mostly involving session fixation or stealing.

URLs also "leak" out of your application freely - they're sent to other servers in REFERER headers, appear in logs etc, but cookies do neither (normally).

Mark

Share this post


Link to post
Share on other sites
Sign in to follow this