One of our clients has requested that their users should be able to remain logged in for 30 days; that is to say if they don’t visit within 30 days they’ll be logged out but if they visit regularly they won’t ever have to keep logging in. It seems like a reasonable request and many websites work this way but what’s the best way to do it?
We’re using Apache (CentOS)/PHP and storing sessions in a MySQL database. What we’ve done so far is to set the session cookie to expire after a year and the session array contains a timeout value which is checked each time a page is accessed then set to 30 days.
It seems like the GC routine was removing sessions too soon so we increased session.gc_maxlifetime to 2678400 (31 days) and this is pretty much working except our sessions table contains 1.6m rows and is 6.5Gb.
So it got me thinking there must be a better way. What’s the common method of doing this kind of thing?
I suggest you to create two tables.The first table to contain the past 15 days and the second the last 15 days per user.So if the session is not update within 15 newest days then transfer it to the “oldest” table and delete it from the “newest”.So with this way you can have more flexible and faster results.
Do not try to solve this feature request by extending the session ttl. This open up another can of worms regarding session hijacking, where time is no longer a limiting factor, making it a major security hole.
A few recommendations.
Keep a database table of valid accesses, one per member if they have a cookie set. The table should contain information that will allow you to validate if the cookie passed along is valid.
On every X requests from the member or on every successful login using the cookie, update/replace the cookie with a new one to make it harder to abuse this as a security weakness.
The cookie name and content should be obfuscated in the way that it should not explain what it is. Though security wise, it will only protect against people that does not know anything about programming.
Security wise it is best if the solution you move ahead with is able to validate the content of the cookie, instead of just basing it off a hash. I.e. have a logic in the back that allow you to verify the content, making it for example an encrypted string, where the IV is changed on every update etc. and stored in the table.
Store everything you need in one cookie, but remember how cookies work, so try to keep the size down. Example on a format: [user id]a[encrypted string, or hash]
That’s a very good point. There are a few thousand logged in users but plenty more guests, and I guess that will include bots. We start a session for every page view although at the latest count there are around 60,000 empty sessions.
According to Google Analytics we get about 150,000 unique users a month so this figure does seem very odd.
I’ve checked the access time and the oldest is 31 days, so old sessions are being deleted.
I’m not sure that really solves the problem to be honest. Access time might improve slightly but we’ll still have a huge amount of data.
Isn’t this effectively the same as using a session cookie to look up the user details?
Wouldn’t session_regenerate_id achieve the same thing?
This is where using cookies might fall down as we typically store quite a lot of data in the session (around 8kb) so we’d have to split it over multiple cookies, which would have to be sent with each request.
What you’re suggesting sounds interesting but I don’t quite understand how it solves the issue of session hijacking? If a malicious user is able to obtain a logged in user’s cookies, won’t they still be hijacking the “session”?
I think we have a misunderstanding here. With the approach I explained, the cookie is only used to allow a member to be automatically logged in when they come back to the website. After they are logged in the normal sessions take over.
So this is the scenario that happen when the member come back to the website after being away long enough for their session to expire. (Note. session time should be set short so 1-2 hours).
The system check if the automatic login cookie was passed.
a) If, it now validate if it is correct and valid
b) If it is valid, it create a login session for the member and recreate the automatic login cookie with new security values.
From this point on, the system operate off the sessions and the login cookie is no longer used since the member is logged in.
No, as the session is not regenerated unless the member is browsing the website. This means if I visited the website for the last time today on December 11th, someone could still hijack my old session on December 20th if you allow the ttl to be that long. While on a normal server, 1-2 hours after I stopped browsing it would not be possible to hijack the session any longer.
Look above, believe my answer there explains the misunderstanding.
This is correct, and that is why I mentioned point 4. Security is still a vital part, but security wise it is significantly easier to solve this with the solution I suggested. I assume your worried about XSS giving away the content of the cookie, the easiest way protect against it, is create two cookies.
Is same for all members, and contain their member id, ref id etc.
The name of this cookie is random, and change every time the login cookie is created, with the cookie name stored in the database tied to the member. The system this way know where to look for the cookie, and then validate it as before.
Note. It is still possible to get the cookies if your website is wide open for XSS, but consider it will take more code, and specific code to do this, it is much harder to do assuming the website tries to prevent XSS.
Good question. It’s difficult to tell but I can create an isolated dev server and test it out.
Ahh, gotcha. Yep, that sounds like a good idea! I’ll give that a go.
Do you happen to know of any tutorials for this kind of thing, or does it have a name? I searched before posting originally and couldn’t find anything but maybe I was looking for the wrong thing. It seems like a common thing to do.
I think that whatever your are going to do the amount of users and the data capacity are still to be huge.So…An another thought is to create multiple same tables to put there the sessions with a logical series.You must set manually a session id with an algorithm (or function) and then input the session into the corresponding table.I am thinking that a tables of 50000-100000 users is more easy manipulating from an one of 1600000+.The session id that you set it must contain the number of the table you are going to be input so with a simple function later you will help the engine to target the session quickly.
I am sorry, I am not aware of any tutorials for this, I am certain there is somewhere, but its years since I stopped reading tutorials so no idea what is good places to look for them these days.
In general it is not difficult, as you just use normal cookies, the important thing is to have a good way to verify it actually exist to the user that come with it. Another thing I did not mention, when using this to automatically login the member, it is a very good idea that updating the profile etc. require them to fill in their password.
With the solution he will look into, he will not have that many sessions as they will be destroy by garbage collection when they expire. But even if, handling 100,000 sessions vs 1,600,000 sessions in a table will have no significant impact on speed. You need a lot higher numbers before table sharding at this level will be worth the effort (I.e. you normally do it when you actually require multiple servers to handle the content in the table, you do not divide it up and keep it on the same database server).