How safe are PHP session variables? - php

I have a login script that verifies a username/password against data in a 'user' table. Furthermore, I have a 'roles' table that specifies the access level of a given user. Assuming I am using safe login scripts, are there any security holes in simply performing an additional query, upon successful login, against the 'roles' table to discover the user's authorization level and storing this into a session variable? The idea would then be that on any page with mixed authority, I could simply query the session variable to discover the logged in user's authorization level.
Thanks.

Sessions are significantly safer than, say, cookies. But it is still possible to steal a session and thus the hacker will have total access to whatever is in that session. Some ways to avoid this are IP Checking (which works pretty well, but is very low fi and thus not reliable on its own), and using a nonce. Typically with a nonce, you have a per-page "token" so that each page checks that the last page's nonce matches what it has stored.
In either security check, there is a loss of usability. If you do IP checking and the user is behind a intranet firewall (or any other situation that causes this) which doesn't hold a steady IP for that user, they will have to re-authenticate every time they lose their IP. With a nonce, you get the always fun "Clicking back will cause this page to break" situation.
But with a cookie, a hacker can steal the session simply by using fairly simple XSS techniques. If you store the user's session ID as a cookie, they are vulnerable to this as well. So even though the session is only penetrable to someone who can do a server-level hack (which requires much more sophisticated methods and usually some amount of privilege, if your server is secure), you are still going to need some extra level of verification upon each script request. You should not use cookies and AJAX together, as this makes it a tad easier to totally go to town if that cookie is stolen, as your ajax requests may not get the security checks on each request. For example, if the page uses a nonce, but the page is never reloaded, the script may only be checking for that match. And if the cookie is holding the authentication method, I can now go to town doing my evilness using the stolen cookie and the AJAX hole.

Only scripts executing on your server have access to the _SESSION array. If you define the scope of the session cookie, you can even restrict it to a specific directory. The only way someone besides you could get that session data is to inject some PHP code into one of your pages.
As for the system you're using, that is acceptable and is a good way to save database calls, but keep in mind that it will require the user to log out and log in again for any authorization changes to apply. So if you wanted to lock out an account and that user is already logged in, you can't.

It should be noted that in Apache the PHP $_SESSION superglobal is accessible across virtualhosts. Consider this scenario:
Your server hosts two domains, example.com and instance.org. PHP sessions are stored in cookies that are restricted to the domain.
A user logs in to example.com and receives a session ID. Example.com sets some session variables (which are stored on the server, not in the cookie).
A third party intercepts the cookie during transmission and passes it to instance.org. Instance.org now has access to the example.com session variables.
This is not such a big deal when you control all the virtualhosts on your server, but if you are on a shared machine, it's problematic.

If you rely on a value stored inside of a session variable to determine roles then you lose the ability to change the value in the DB and have it reflected against the user's current session. If you look at Zend Framework, there's a clear distinction between authentication and authorization, and strongly-worded warnings in the manual to only store the minimal amount of data in the session (ie "Yup, he's user #37 & he logged in").
As far as 'safety' goes - unless you're on shared host, there's nothing to worry about. On a properly configured shared host, they should be relatively secure, too.

Related

How do I secure user login in PHP

So I have my database with the standard usernames and hashed passwords (password_hash).
There's a login form on my site, where users type their details and a session gets created based on that.
<?php
session_start();
//if the username and password is valid
if(validLogin){
$_SESSION['account'] = $username;
}
//$_SESSION['account'] is used from now on for backend user activities
?>
If the credentials are correct, an account session variable is created pointing to the username.
I have increased Session lengths to 1 month (as users complained they kept getting logged out before)
What can I do to increase security here?
If I go into Dev Tools, there is only a single cookie called PHPSESSID, which holds a 26 character value.
However, anyone can just copy and paste that value into their own browser and hijack someone's account - if they had the value.
I am not sure what to do and quite lost.
How can I improve the security here? Besides logging out users every 24 minutes
Firstly, The concept of a session (originally) implied:
The session begins when you open an application, continuous as long as
you work on it, and ends when you close the app.
Since the nature of apps changed and for convenience reasons, this basic pattern has been extended, and sessions kept alive much longer, even if the user is not actively working on it. However, if security is your main focus use a more strict approach, i.e. shorter expiration time and forced logouts.
Secondly, PHP sessions have been invented to solve many of the problems that plague regular browser cookies (stored on the client as text, limited to 4KB, rather accessible, sent along with every request, hence bloating the HTTP request). A PHP session only sends a hashed ID to the browser, and stores all data on the server. If an attacker owns the client security is indeed compromised. But this may be out of your hands if you cannot control the client device security. Security is a multi-layered concept that implies varying degrees of trust..
This answer gives more context on PHP session security and has some useful security recommendations. However, I have one additional recommendation: If you do not need to work with the PHP session id in your client code (i.e. JavaScript) disable session.cookie-httponly in your php.ini (or alike).
If this all is not 'good enough' you will need to implement a more secure protocol, use multi-factor-authentication, or alike. In general, session ids are probably your least concern; having a properly configured web server and following best practices is more important.

PHP - encrypt other site's username and password

I am programming a PHP site that allows users to register, and both registered and unregistered users can enter their respective usernames and passwords (for example smith8h4ft - j9hsbnuio) for school site.
Then, my PHP script sends some $_POST variables, downloads and parses the marks page, making an array called:
marksDB = Array("subject" => Array("A", "B", "A", "C"), ...), and writes it reformatted.
My question is:
How should I keep the username and passwords safe?
For unregistered users, I currently forget username and password and put the marksDB into $_SESSION. When user is inactive for e.g. 30 minutes, marksDB is deleted. How safe are these data in $_SESSION ? And how about users that log in, view page once, and never view it again, so the script doesn't delete the marksDB from session? Is the session deleted automatically (gc.maxlifetime)?
And what about registered users? I want to have everything safe, but I don't want to annoy user with password prompts every 30 minutes of inactivity. Is it safe to encrypt credentials like described here, but without the third user-set password? Or have I to ask the user for his password every time?
EDIT:
Thanks for quick replies,
#Justin ᚅᚔᚈᚄᚒᚔ : I doubt they have some API, but I can ask them, just for case
#Abid Hussain: Thanks for very useful links. (Thanks both for answers too).
I will throw users' credentials away and have only parsed markDB, which I will probably throw away too (after logout or inactivity) - it is cheap to retrieve marks again when needed.
If the school site doesn't expose an API for this (for example, using OAuth like the StackExchange sites do), then your options are limited.
Generally speaking, it is never a good idea to keep a user's plaintext credentials for longer than is absolutely necessary. There are security implications for any possible way you can imagine to try to do it (session hijacking, stolen keys, decryption, etc).
A better approach might be to make the marks download process strictly user-initiated. Give them a button that says "retrieve my marks", and go through the authentication process there, download the marks, and throw away their credentials. Each time they "sync", they should have to authenticate. Unless the marks change on a frequent periodic basis, there should be no reason you can't download all the information you need at once and then cache it securely on the server for later usage.
session files will be deleted by the garbage collector after a certain time, but a good rule of thumb for storing in _SESSION is only store data that you would output on the screen, i.e. the password is probably not something you want to store in the session. Session files can be read from the server and it's possible for some nefarious user to hijack the session and see things they are not supposed to see or even somehow see a var_dump($_SESSION).
If you want to allow registered users longer sessions you can have periodic page refreshes with JS (not necessarily refreshing the page .. just an asynchronous request will do) or perhaps even increase the session time with ini_set if allowed. It's not necessarily safer to ask for passwords repeatedly .. it depends on how vulnerable the password is when you are asking.
Another solution is to have the infamous "Remember Me" cookie keep the users logged in.
Passwords are not for decrypting. Encrypt for secrecy. Hash for authentication.
Everything in the session is server side, so it's not accessible by others. However, sessions can be 'hijacked' as explained here.
You could increase the length of the session in your PHP.ini or use periodic AJAX calls on the background to keep the session alive. The sessions are deleted when they are expired by the server.
Encrypting a password so it can be decrypted is usually frowned upon unless there is no alternative. With encrypting, not only you, but also everyone else with access to your database and/or source code can retrieve the passwords.
See URL
http://phpsec.org/projects/guide/4.html
http://www.sitepoint.com/blogs/2004/03/03/notes-on-php-session-security/
http://talks.php.net/show/phpworks2004-php-session-security
http://segfaultlabs.com/files/pdf/php-session-security.pdf
safest way to create sessions in php
Also Read it
Sessions are significantly safer than, say, cookies. But it is still possible to steal a session and thus the hacker will have total access to whatever is in that session. Some ways to avoid this are IP Checking (which works pretty well, but is very low fi and thus not reliable on its own), and using a nonce. Typically with a nonce, you have a per-page "token" so that each page checks that the last page's nonce matches what it has stored.
In either security check, there is a loss of usability. If you do IP checking and the user is behind a intranet firewall (or any other situation that causes this) which doesn't hold a steady IP for that user, they will have to re-authenticate every time they lose their IP. With a nonce, you get the always fun "Clicking back will cause this page to break" situation.
But with a cookie, a hacker can steal the session simply by using fairly simple XSS techniques. If you store the user's session ID as a cookie, they are vulnerable to this as well. So even though the session is only penetrable to someone who can do a server-level hack (which requires much more sophisticated methods and usually some amount of privilege, if your server is secure), you are still going to need some extra level of verification upon each script request. You should not use cookies and AJAX together, as this makes it a tad easier to totally go to town if that cookie is stolen, as your ajax requests may not get the security checks on each request. For example, if the page uses a nonce, but the page is never reloaded, the script may only be checking for that match. And if the cookie is holding the authentication method, I can now go to town doing my evilness using the stolen cookie and the AJAX hole.
The session file is server side so it should be invisible to clients. But they still can trick your program into using another session if they know the session ID.
For the registered users you can store the password in a DB or a file after you have encrypted it with a key that only you know (maybe a new one generated randomly and stored for each user)

How exactly does session hijacking work in PHP?

I've made a website which has registration/login. I can see the PHPSESSID cookie in Chrome's Developer Tools, so I'm wondering how can I use this session id value to hijack into the account I'm logged, from let's say a different browser, for simplicity's sake?
Should a secure website be able to determine that this session is being hijacked and prevent it?
Also, how come other big sites that use PHP (e.g. Facebook) do not have PHPSESSID cookies? Do they give it a different name for obscurity, or do they just use a different mechanism altogether?
Lots of good questions, and good on you for asking them.
First.. a session is just a cookie. A 'session' is not something that's part of the HTTP stack. PHP just happens to provide some conveniences that make it easy to work with cookies, thus introducing sessions. PHP chooses PHPSESSID as a default name for the cookie, but you can choose any you want.. even in PHP you can change the session_name.
Everything an attacker has to do is grab that session cookie you're looking at, and use it in its own browser. The attacker can do this with automated scripts or for instance using firebug, you can just change the current cookie values.
So yes, if I have your id.. I can steal your session if you didn't do anything to prevent it.
However.. the hardest part for an attacker is to obtain the cookie in the first place. The attacker can't really do this, unless:
They have access to your computer
They somehow are able to snoop in on your network traffic.
The first part is hard to solve.. there are some tricks you can do to identify the computer that started the session (check if the user agent changed, check if the ip address changed), but non are waterproof or not so great solutions.
You can fix the second by ensuring that all your traffic is encrypted using HTTPS. There are very little reasons to not use HTTPS. If you have a 'logged in' area on your site, do use SSL!!
I hope this kind of answers your question.. A few other pointers I thought of right now:
Whenever a user logs in, give them a new session id
Whenever a user logs out, also give them a new session id!
Make sure that under no circumstances the browser can determine the value of the session cookie. If you don't recognize the cookie, regenerate a new one!
If you're on the same IP and using the same browser, all you have to do is duplicating the session ID (and maybe other cookie values: not really sure if browser specific things like its agent string is tracked/compared; this is implementation dependant).
In general, there are different ways to track users (in the end it's just user tracking). For example, you could use a cookie or some hidden value inside the web page. You could as well use a value in HTTP GET requests, a Flash cookie or some other method of authentication (or a combination of these).
In case of Facebook they use several cookie values, so I'd just assume they use one of these values (e.g. 'xs').
Overall, there's no real 100% secure way to do it (e.g. due to man-in-the-middle attacks), but overall, I'd do the following:
Upon logging in, store the user's IP address, his browser agent string and a unique token (edit due to comment above: you could as well skip he IP address; making the whole thing a bit less secure).
Client side store the user's unique id (e.g. user id) and that token (in a cookie or GET value).
As long as the data stored in first step matches, it's the same user. To log out, simply delete the token from the database.
Oh, and just to mention it: All these things aren't PHP specific. They can be done with any server side language (Perl, PHP, Ruby, C#, ...) or server in general.
Someone sniffs the session ID cookie and sets it for a subsequent request. If that's the only thing authenticated a user, they're logged in.
Most sites will use authentication based on cookies in some form. There are several ways to make this more secure such as storing info about the user's browser when they log in (e.g. user agent, IP address). If someone else naively tries to copy the cookie, it won't work. (Of course, there are ways around this too.) You'll also see session cookies being regenerated periodically to make sure they aren't valid for a particularly long time.
Check out Firesheep for a Firefox extension that performs session hijacking. I'm not suggesting you use it, but you may find the discussion on that page interesting.

What to store in a session?

I know about all the issues with session fixation and hijacking. My question is really basic: I want to create an authentication system with PHP. For that, after the login, I would just store the user id in the session.
But: I've seen some people do weird things like generating a GUID for each user and session and storing that instead of just the user id in the session. Why?
The content of a session cannot be obtained by a client - or can it?
You're correct. The client just sees a randomly generated session id token. There are ways this token can be misused (hijacked, etc.), but having a GUID on top adds nothing. In contrast, options like session.cookie_httponly (JavaScript can't see session cookie) session.cookie_secure (Cookie can only be transmitted over HTTPS) protect against certain attack scenarios.
The short answer is that $_SESSION is safe and you do not need to worry about its contents being leaked to a user or attacker.
The content of the session is not normally be accessible to the user. You should be able to store the user's primary key and you'll be fine. There are cases where the session can be leaked, on a normal linux system the session folder is in /tmp, however this could be changed in your php.ini to the web root (/var/www/tmp) and then could be accessible. The only other way is if the user is able to get access to the $_SESSION super global by hijacking a call to eval() or by the variable being printed normally.
If you are running on a shared host and using an old version of PHP and/or your server is misconfigured it might be possible for another user on this system to read or even modify a session file stored in /tmp/. I don't know of a single application that takes this attack into consideration. If this is a problem you can store the information in a session table in the database.
Sometimes, for added security, developers may assign a long string to the user's session in order to make hijacking even more difficult. By setting a cookie with this new string at the time of session creation, the app can check for the correct string on subsequent requests to better ensure it is the person who actually logged in.
It's just adding one more thing a wannabe hijacker would have to guess. However, it can be a false sense of security as it does little to protect the session if sniffing is involved because the new cookie is sent right along with the php session cookie. Also, session id's are very hard to guess as it is (as I'm sure you know, just don't place it in the url but, rather, in the cookie).
Session info is stored on the harddrive so it's not obtainable by clients without application intervention.
I've never seen GUIDs being used for sessions, but there are a couple of additional methods I have seen that do add a little more security.
Storing the user's IP - if you need to force a session change based on locations (sometimes geoIP stuff will do this)
Storing the user's HTTP_USER_AGENT header string. Can provide a bit of security against hijacking if the hijacker happens to be using a different browser.
There's a great article on session hijacking countermeasures on Wikipedia, actually.
That being said, I would imagine that anyone storing a GUID as part of a session to use in session security might be failing to see a better solution (such as session regeneration). I can see other uses for a GUID to be stored (maybe it's part of a random generator for a game), but not for use with session security.

Cookies/Sessions login system

When a user logins I get him/her's ID and save it in a session var. What I wonder is, is this the way to go? Or should I use cookies? so it automatically login and so on.
session_start();
ifcorrectlogin {
$_SESSION['id'] = mysql_result($loginQuery, 0, 'user_id');
}
how do you authenticate your users?
//Newbie
Yes, this is the way to go. The session itself is already backed by a cookie to remove you any programming efforts around that. The session (actually, the cookie) will live as long as the user has the browser instance open or until the session times out at the server side because the user didn't visit the site for a certain time (usually around 30 minutes).
On login, just put the obtained User in the $_SESSION. On every request on the restricted pages you just check if the logged-in User is available in the $_SESSION and handle the request accordingly, i.e. continue with it or redirect to a login or error page. On logout, just remove the User from the $_SESSION.
If you want to add a Remember me on this computer option, then you'll need to add another cookie yourself which lives longer than the session. You only need to ensure that you generate a long, unique and hard-to-guess value for the cookie, otherwise it's too easy to hack. Look how PHP did it by checking the cookie with the name phpsessionid in your webbrowser.
Cookies can be manipulated very easily. Manage login/logout with Sessions. If you want, you can store the users emailaddress/username in a cookie, and fill the username box for them the next time they visit after the present session has expired.
I would try to find a session engine so you don't have to deal with the misc. security issues that bite you in the ass if you do the slightest thing wrong. I use django which has a session engine built in. I'm not aware of the other offerings in the field although I would assume most frameworks would have one.
The way they did it in django was by placing a cryptographic hash in the user's cookies that gets updated every page view and saving all other session information in a database on your server to prevent user tampering and security issues.
As BalusC mentions, the session_-functions in php are the way to go, your basic idea is sound. But there are still many different realisations, some of them have their pitfalls.
For example, as Jonathan Samson explains, using cookies can result in security holes.
My PHP is a bit rusty, but I remember that the session_-functions can also use session IDs that are encoded in URLs. (There was also an option to have this automatically added to all local links (as GET) and form targets (as POST). But that was not without risks, either.) One way to prevent session hijacking by copying the SID is to remember the IP address and compare it for any request that comes with a valid session ID to to IP that sent this request.
As you can see, the underlying method is only the start, there are many more things to consider. The recommendation by SapphireSun is therefore something to be considered: By using a well tested library, you can gain a good level of security, without using valuable development time for developing your own session system. I would recommend this approach for any system that you want to deploy in the real world.
OTOH, if you want to learn about PHP sessions and security issues, you should definitely do it yourself, if only to understand how not to do it ;-)

Categories