I'm writing a user system where users will log in using Twitter's API, then I'll store the information in a database along with a few extra pieces that I have the user put in. I want the user to be able to come back after logging in and not have to log in again. I decided that I'd get all the relevant information about the user, save it to the database, then save the session ID and user ID to another table. Finally, I'd set a cookie on the user's computer containing the same session ID so that throughout their browsing they would stay logged in. Then if they closed the browser and revisited the site later, I would read that cookie, get the compare it with the sessions table, get the User ID, and reconstruct the session (updating the sessions table with the new session ID).
My question is, how random is the session ID? Is there a possibility that a user might get the same session ID that a user that hasn't visited the site in a week (so the cookie would still be active) had assigned to them? If this happens, then the server might mistake the new user for the old one. I really would like to avoid using the IP address because people might visit the site from a mobile browser where the IP can change at any time.
Any ideas on this? I just want to ensure that user A and user B, separated by any amount of time, won't get the same session ID.
Append current time in microsecond to the unique id...
session_id() + microtime();
So not only would the session_ids have to be the same, it would have to happen on the same microsecond... making the vanishingly unlikely just about impossible. The only way to guarantee it 100% is to check this random value against all existing session ids and re-roll it if it already exists.
Although the probability of having two active sessions with identical identifiers at the same time is vanishingly low (depending on the hash function), you could add an additional (pseudo-) unique value to that session ID to get a value with both characteristics.
You could use uniqid that fulfills the latter:
uniqid(session_id(), true)
uniqid’s value is based on microtime with an additional pseudo-random number from lcg_value and an additional source for more entropy that all together guarantees unique values.
The PHP Session ID is an MD5 hash, which makes it 128 bits in length. That's something like 340,000,000,000,000,000,000,000,000,000,000,000,000 different possibilities. The odds of two people getting the same one are pretty remote.
If you want to guarantee uniqueness, put something in their cookie based on sequential numbers.
Related
I would like to know if i can prevent session id cookie malformation. Let's say I have a cookie with key SESS and value of md73i54kj98ti0uf8dftps2fa3 which is a valid session id and corresponding file sess_md73i54kj98ti0uf8dftps2fa3 exists in my session storage folder. If I modify value for key SESS cookie to be for example foo it will create new file in sessions folder with name sess_foo. How can I check that provided cookie sess id value is invalid so that I can call session_regenerate_id for example to set valid id and create appropriate file.
Also, I am wondering if someone hypothetically renames cookie sess id to real session of another user will he get control? are there ways around this?
Thank you.
Update 1: First problem can be solved with session.use-strict-mode ini directive. http://php.net/manual/en/session.configuration.php#ini.session.use-strict-mode
Though it requires some extra steps when using with custom session handler. (my case).
But still what if changed session id matches real session id of other user? Should I use some kind of fingerprinting (user-agent + ip) or encrypt session data with combined key of (user-agent + ip)?
For new session files being created, you already have the solution which is session.use-strict-mode.
But what about guessing another session id? You are totally right, if you can guess another valid id, you will be using that session, effectively impersonating its owner. After login, the session id is the only secret used by the user, equivalent to the userid+password for the session.
Why is this not a problem then? Because you can't reasonably guess another valid session id. Session ids are (should be) cryptographically random, and so long that you can't just guess one (more precisely, they have a lot of entropy). Standard solutions, like the one in PHP and most other programming languages or frameworks provide a reasonable level of security, but you should not implement your own session management (id generation, verification, etc.), unless you really know what you are doing and are aware of the security aspects.
In your example, the session id seems to consist of 26 lowercase letters and numbers. There are 26 different letters and 10 numbers, so the number of possible session ids are (26+10)^26 = 2.9 * 10^40. Say you can try one billion (10^9) ids every second, and your server has one million sessions at once (neither of this is realistic in any way). It would still take around 10^25 seconds (~ 3*10^17 years) to correctly guess a valid session id. Note that this is way more then the age of the universe. :)
Of course, for the reason above (session id = username+password for the session), you must protect the session id as much as you can, for example by only storing it in an httpOnly cookie, and never sending it in the URL, or clear text (but always using https, with the secure flag for the cookie, and HSTS headers sent, etc).
I was using $_SESSION to store users IDs but I want to change to using a regular cookie (with a KEY) paired with SQL so I can keep users authenticated when they close their browsers.
How do I create an unique key for each row in my SESSIONs table?
You're effectively looking to write your own session handler to bypass PHP's cookie expiration issue on session cookies. This is pretty straightforward, and pretty optimized if done properly.
Step 1: generating the session ID
A session ID is unique. However, if you're planning for permanent sessions, you have to bear a couple of things in mind: you want the session to carry over per browser, regardless of connection issues. So, you can cheat the system a bit by mapping the session ID with the user agent of the navigator (which doesn't change).
This allows you to reduce the chance for your session ID generator to generate the same ID for two distinct visitors. The rest is up to a random number generator and a hash algorithm, though - md5(microtime().$_SERVER['REMOTE_ADDR']) is usually a trusted friend.
Step 2: storing and retrieving the data
Storing the data is also trivial. Your aim is to create a MySQL table with a minimum of two columns: the session key (set to PRIMARY), and the data (serialized array for the simplest form, stored as TEXT).
When you create a session, just insert a new row and watch for errors. If an error comes up, the key was already used, so you'll need to re-generate another. If the row was inserted successfully - all good, you now have a session row for the user! All you need to do from there on is to read/write to this row as you see fit.
Cookie the user with the session ID, and you're done!
Caveats
Never ever use a userID as session ID. Cookies can be very easily manipulated. You want the cookie value to be random and completely separated from the user. It should be meaningless.
You'll need to write clean-up code to clean up the session table from time to time. A good way to do this is to keep track of when a session was last used - and delete accordingly.
The best way is to use some hash function, which is actually also used by PHP itself by generating session id's.
You can use something like this:
md5(microtime());
You can combine more values together, e.g. random number, user agent, server name or use stronger algorithms like SHA2.
But for uniqueness you will always need to look into table if there isn't such hash already stored (but the probability is very low of course).
But I would just go for standard session_id() which does all the job.
You can use MySQL's UUID().
If you want to keep the session alive you can also use PHP's session_set_cookie_params().
The SQL solution has one advantage though: you can scale to more webservers easily. On the other hand you can not use PHP's $_SESSION array and you have to write the session handling yourself.
This is a design question relating to a website I'm building.
I have a 'Player' table that will store names, passwords, last IP, dates of birth, links to avatars, locations, etc.
When the player logs in, the database will be searched for the username they entered, and their password will be checked out as well (yes, of course the passwords are hashed). Then, they will be given some cookie that will keep them logged in.
Every time they visit a new page, the correct player will be looked up in the database using the information in the cookie. If their current IP matches the last IP they logged in with (probably 10 seconds ago), the page is outputted with their name on it and whatnot.
Here's my question: should I have the primary key for the Player table be the player's name (a text field that I know will be unique), or should I create some arbitrary auto-incremented index for that?
Keep in mind that this also has an effect on the information stored in the cookie - whether to store an int or the user's name in text. As well, I want to do some sort of hashing on that value (just for a little added security), so that the cookie doesn't just contain the int or the username.
So, in terms of both efficiency and design, which is the better choice?
EDIT Using VARCHAR for the database would also be ok, and probably faster, I imagine.
EDIT2 This primary key will also be referenced by other tables.
As Marc's comment indicates, int will be more efficient for both memory and performance.
I'd recommend against tying logins to IP addresses, some users will have each request to the server come from a different IP address (onion routers, AOL, who knows what other kind of weird corporate NATs), and being logged out all the time will be super annoying.
You may also want to consider using sessions instead of setting a cookie saying who they are logged in as. Even though having a sig would make it a bit more secure, using sessions would be safer still, along with giving you more flexibility to track information about users before they log in (for example, what page they should be redirected to after a successful login).
Here's my question: should I have the primary key for the Player table be the player's name (a text field that I know will be unique), or should I create some arbitrary auto-incremented index for that?
The latter.
Names tend to change.
whether to store an int or the user's name in text.
As well, I want to do some sort of hashing on that value
Please make your mind first, then ask
is it going to be hash or a plain value finally? If the latter - what's the difference then?
So, in terms of both efficiency and design, which is the better choice?
Oh. This one absolutely doesn't matter.
Correct me if I'm wrong please:
Sessions will last a finite amount of time (refreshed from the server every 15 or so minutes until the browser is closed) - more secure/short term
Cookies on the other hand can last until the browser closes or to some specific time in the future - least secure/long term
Given this, how do allow a user to close his/her computer, come back a few days later and open a site and still be logged in using cookies and still somehow being secure?
How does someone like, amazon for instance do this?
EDIT:
To be more clear, here is an example:
if (!isset($_SESSION['id']))
{
$_SESSION['id'] = $_COOKIE['id'];
$_SESSION['email'] = $_COOKIE['email'];
}
this is obviously bad, what is a better way?
First of all, "session" is more of a concept rather than a concrete implementation.
For PHP I believe the default way that session data is stored on the file system and it is associated with a session id that is usually stored in a cookie (although it can also be sent via query string parameter:http://stackoverflow.com/a/455099/23822).
It's also possible to store session in a database. Doing so allows you full control over how session data is stored and when it expires so you can have sessions that last as long as you want them to. You just need to make sure the session cookie also shares the same expiration time as the data in the database.
As to your question of in a comment about "What's stopping someone from grabbing the cookie data and falsifying a login using the token?" Theoretically that can happen, but the session ID stored in the cookie should be random so it would be highly unlikely that it would be guessed (an attacker would have a much easier time guessing the user's password). In fact the same thing is already possible with any kind of session.
Sessions expire mostly because keeping them open on the server is inefficient. You need a cookie (or some other mechanism, but cookies are usual) to associate them with a browser anyway.
Amazon handles security by having three levels of "being logged in".
Level 1: Basic functionality just works.
Level 2: You must reenter your password to access some things (e.g. order history)
Level 3: You must reenter payment information to access some things (e.g. adding a new delivery address)
For the cookies I use the method described here:
http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/
and here: http://jaspan.com/improved_persistent_login_cookie_best_practice
You store a session-based security token inside the user's cookie data, and store that same token in the user's db table. At session creation, you check if this username/token pair can login to your website according to the previously stored data, and then invalidate the token.
While creating my website i was stuck on a thing.
Wether i should use $_COOKIE or the session.
I thought using using $_COOKIE would be better.
But what should i store in cookie the users username or the user's unique id ?
And how much time forward i should put the time of the cookie ?
And should i forward the same time on each page or different ? If different then how much ?
It ultimately comes down to whether your website/application needs to be stateless or not. (See Webservices are stateless?). Its mostly a design decision, but I prefer stateless applications where possible.
If you do use cookies here are some tips:
You want to store data in the cookie that will uniquely identify the user, but something that is not able to be guessed.
It is common to put a user_id or a username (provided the user is unable to change it) and a random hash stored alongside the row in the database. When it comes to logging a user in load the user by their user_id and check that the hash in the cookie matches the one in the database.
As far as how long to store it for, that depends on the nature of your application. If it contains sensitive information then its probably not a good idea to make it last for a long time. You should update the time each time the users requests a page so if a user is using the site they will remain logged in for the duration of their visit.
It is really important not to put sensitive information in cookies, because they are stored in plain text on the user's computer.
You've not provided any information relating to the reasons for your choice of data substrate nor any indication of what you are trying to achieve ("php optimising members after login" - is meaningless gobbeldy-gook).
Wether i should use $_COOKIE or the session.
Hopw much data are you trying to store? For how long? Do you require to have access in the absence of a session? If so does the data need to be available? What is the impact of the user changing the data outwith your website?
But what should i store in cookie the users username or the user's unique id ?
Neither - if your site believes the assertion in the cookies, then hacking your site is as simple as changing the cookie value.