Let's clarify a few things first:
PHP sessions, in their default form are cookie based, I have an overview of how they work in another answer. But since they are cookie based, to me that says that if you are going to base your authentication and authorization workflow around cookies, go with normal cookies for everything - and just use sessions for what they are good for: session data persistence.
The circular login pattern you noticed is exactly why you should delete cookies on logout. Also, as @Morons writes, when a user clicks log out, expects to actually log out. It doesn't cancel out the auto-login feature, but one should be able to log out at any time.
Now, my auto login-approach goes something like this:
Which of course is pretty basic, but what is really important is the whole "cookie is valid" check. When a user successfully logins with the "remember me" option checked, I generate an auto-login token: A sensibly unique random string, most probably a salted hash.
That's the only thing I store in the cookie, nothing else nothing more. There is no actual need to store anything that could possibly identify an individual user in a cookie, you should avoid storing stuff like usernames and emails (even hashed). In my database, I have a simple token
table that basically stores:
- A user id,
- The token,
- An expiration date
And my check involves just checking against this table. The expiration date may seem like an overkill, as you can expire the cookie whenever you want but: You should treat everything that comes along with the cookie as user input, unsafe and easily faked. That includes it's expiration. And of course at log out I don't bother killing the cookie, I just set a NOW()
as the expiration date. Easy as pie, and I keep most elements of the check server-side, where it feels a little bit more safe.
In a recent project, I went a step further and added the check at every page request instead of storing something in a session to indicate an authorized session. Some may argue that the call to the database will lead to performance issues, but my 'token' table has grown to about 10 million tokens1 and still I haven't noticed any actual issue. Keep in mind that sessions involve file system access, that's almost never faster than a simple select
.
Of course, any cookie that stores anything related to authentication and authorization should be encrypted. And I'd recommend you always enforce secure cookies, with setcookie()
is as easy as setting the secure
parameter to true:
Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client. When set to TRUE, the cookie will only be set if a secure connection exists. On the server-side, it's on the programmer to send this kind of cookie only on secure connection (e.g. with respect to $_SERVER["HTTPS"]).
1 I don't delete expired tokens for a variety of reasons.
Are you actually using sessions to store data for non-authenticated users, or are you just calling session_start()
on every page load? If that's the case, just stop initializing sessions for every visitor, and only do so when a user actually logs in.
Only start a session if one already exists:
if (session_status() === PHP_SESSION_ACTIVE) {
session_start();
}
Start session at login:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($_POST['username'] == 'foo' && $_POST['password'] == 'bar') {
session_start();
$_SESSION['username'] = $_POST['username'];
}
}
However, if you are in fact storing session data for even non-authenticated visitors, it gets a little trickier. You can't just set a custom TTL for individual sessions. You could certainly modify the session expiration length to be several days or weeks, but this would cause PHP to retain the server-side session data for the same length of time.
Your concern about millions of active sessions is understandable, but the actual impact on your server depends entirely on how you've configured PHP to handle sessions. By default, the sessions are stored on the server's file system in a temp directory. Since the session files are minuscule (assuming you aren't doing something horrific like storing BLOB data in them), you aren't likely to use a significant amount of disk space accommodating user sessions for extended lengths of time.
However, if your site experiences constant and heavy traffic, the disk IO is going to flog your server into submission. In this case, I would think about using memcache or redis as the session engine, in order to ease the load on my server's hard disk. But you don't want session data remaining in the server's physical memory for very long, so set the TTL to something relatively short like 1800 (seconds).
Now comes the tricky part: persisting long-term sessions...
When a user logs in, you can send them a custom cookie containing their session id, and with a much longer TTL (or none at all). Then, create a database entry or write a "temp" file containing the serialize()
'd session, and indexed by the session id. You would need to keep this db entry or temp file in sync with the session while the user is active, but once they leave and the in-memory session expires, you still have their session data on your hard disk. When they return, days or weeks later, you can read their cookie value, retrieve the serialized session from your database or temp file, reinitialize the user's session with the original ID, and unserialize()
the session data back into $_SESSION
.
IMPORTANT NOTES: You would need to implement some measures to prevent session stealing, such as comparing the visitor's IP address and/or user agent (browser) against their values at login. You would also need to have some kind of cron job clear out stagnant session data from your db or temp file directory after a given length of time, otherwise it would just keep getting bigger.
Best Answer
Simply store the entire shopping cart and contents in a cookie or other client side storage.
Then you don't need the database and there is no risk of a user gaining unauthorised access.
Plus it will scale better.
The problem with shopping baskets in databases, especially for anonymous users is how long do you hold them for.
During peak times such as a sale you might have millions of users adding things to their shopping bag while browsing. You also have the millions of people who browsed in the last week/month/year.
You have two problems.
By storing the basket client side, you can draw the entire page from static or cached content on a CDN. Saving you a huge amount of server side processing. Which is at a critical premium at sale time.
You also solve the problem of how to keep track of the anon users, because they keep track of themselves via cookies.
Guessing a random anon users shopping basket might seem like a low value target. However auto incs can give away more than you might suspect.
If I am an investor in your stock for example, the growth in that number might be a good insight into your quarterly results before they come out.