PHP, cookies, and rapid browser refresh - php

For my login system, I have a token value that changes every time authentication occurs. Authentication occurs every time any page is accessed (by looking for token cookies and sessions ad such), as well as every $.ajax call (my thought is that I would want to ensure the user is authenticated at all times, and if ever authentication fails with a bad token or series or whatever, the system would automatically completely log out). During the authentication process, when it is determined that the current session is valid, a new token is generated, and that token is set as a cookie as well as updated in a MySQL table, as such:
$newtoken = hash("sha256", mt_rand());
my_mysqli_query($link,
'UPDATE _rememberme SET token = "'.$newtoken.'", lastupdated = "'.now().'"
WHERE series = "'.$series.'" AND email = "'.$email.'"');
setmycookie("token", $newtoken, 7);
When I rapidly refresh the browser, it ends up that the MySQL token and the cookie token do not match. I think that the problem is that during a rapid refresh, the MySQL table gets updated, but then a refresh occurs and the script aborts before updating the cookie. This causes future authentication failures because the cookie token doesn't match the MySQL token.
I would really appreciate some ideas on how to survive a user rapidly refreshing their browser.
I have researched this issue and had little success in finding a solution.

Your solution adds nothing to the security, and adds a great source of headache to your users.
If you use PHP Sessions, you don't have to rely on multiple cookies, and don't have to hash everything on every access. Having sessions in place will increase the security, as your users will not be able to change their session variables. A cookie is user-modifiable, so you can't blindly trust them. A user can change the PHPSESSION cookie, though, but the chances of changing the session to another one is so, so small that is almost impossible.
With your current code, if a user opens a link on a new tab, and before the request returns he opens another link, he will be logged off. The cookie will change on the first request, but before the browser gets the new value, the user submits another request with the old cookie. The new value is on the database, the new request with the old value is processed, and the session is invalidated. Another unhappy user confused because he was randomly logged off.

Related

Best way to prevent simultaneous login

I know this is a recursive question, but, I haven't found a new solution, or a solution based on the new frontend frameworks or technologies.
I've a Vue + PHP application that users can olny log once per time. My current solution to block concurrent access is making a call to a PHP page with Ajax from 5 to 5 minutes storing the time. I store a flag in DB too, whether it has been registered or not. So, when the user try to log in, I check if the time is greater than 6 minutes or the flag is set to 0.
I think this is not the best way to do this. When the application has too many users it can cause too much load on the server.
There is a way to do like Netflix? An warn when triyng to connect and was logged in another machine.
If your end goal is to have it so that any given account can only be logged into one machine at a time, generate a unique ID at login and write that ID to the database for that user. Set that ID as a cookie for the user. When you receive traffic from that user, only consider them logged in if their cookie matches the value in the database.
When the user logs in to a new device, a new unique ID is generated and sent as a cookie to that new device. The new device's traffic has a cookie that matches the database, and is therefore considered logged in. When the old device visits your application, the login cookie no longer matches the value in the database, so that user is considered logged out.
When the old device logs in again, a new unique ID is generated in the database and sent as a cookie to that device. They are now logged in, because their cookie matches. The second device, having its cookie no longer match the database, is logged out.
This solution doesn't require you to access the database on every page, reducing database load significantly.
Add a field for sessionID to your user table in the database.
Set the default session handler before calling session_start() (needed for the next line of code to work):
session_set_save_handler(new \SessionHandler());
On every successful login, retrieve the stored $sessionID from the database. Destroy the old session with:
(new \SessionHandler())->destroy($sessionID);
Get the new session ID with:
$sessionID = session_id();
Store the new session ID to the database.

Cookie stealing

I have read many answers on stackoverflow, but none of them could answer my question.
Let's consider a case, where a teacher and the student use the same computer. The teacher uses it to upload marks and the student use the computer to browse the marks. Now, the cookies of the teacher are stored and accessible. The student can easily forge the cookies and present himself as a teacher to the system and create havoc.
What approach should I take to disable this? Is this only possible via sessions? Or there exists a possibility with cookies as well.
The main two suggestions I would have are:
Delete the entire session right before the user logs out or logs in. A new session should always be started when authorization level changes.
Only accept sessions ids you generate. By default PHP will accept and start a new session for any value of the session id you send it. If you receive a session id you haven't seen before, discard it and send the user a new one.
If it's not necessary to have the browser remeber the cookie between browser sessions (eg a login can be 'forgotten' if the browser window is closed), then you could NOT set the expiry date on the cookie which makes it memory-resident only.
When the user closes the web broswer, the cookie is forgotten (standard browser behaviour) so there's no link to the old session, and a new login must be provided. The old session becomes "orphaned" and (assuming you have a 'expire through inactivity' process) will be expired eventually.
If you need the browser to remember the last user even if the window was closed, then a short-but-appropriate timeout is needed - destroy the session making the cookie useless after x minutes/hours inactivity (whatever is appropriate).

Persistent login using cookies and session variables

I've been researching the best (and safest) ways to implement persistent logins on my website, and I've come up with the following:
When a user logs in, a cookie is created containing the user's ID/username, and a randomly generated number (token). The token is stored in a relational table along with the user ID/username. Every time a members-only page is loaded, this cookie is checked against the relational table, and if it exists and matches with the token, the login is valid and the page can load. If not, however, then the login is invalid, the cookie is destroyed, and the user is prompted to log in.
I was thinking... to save on database access every single time a page is loaded, I could also have a session variable that lasts, say, 10 minutes, and is destroyed automatically when the browser closes. If the session is alive, then it's refreshed and the user can proceed. If the session expires, but the cookie is still valid, check the cookie, reset the token, store that new token in the database (while eliminating the old token, or storing it in an archives table for future reference), and reset the cookie using the new token value.
However, what would the session contain? And how could the session not simply be faked with some JavaScript? Perhaps the session contains a one-way encrypted hash? What would be used to generate that hash (user ID, etc.)?
I'm kind of stuck on where to go from here. I get the cookie stuff, but using temporary sessions (to avoid repeated calls to the database every single time a page is loaded) eludes me. Any help? Thanks.
Cookies should be fine (an alternative would be to store it in the HTTP header), however I don't see the need to store the username/ID in the cookie. The token itself should be enough. You can use a UUID as a token. Store that along with the username and a last_access_timestamp in the database table. And only send the token (in a cookie or in the HTTP request header) on every request. That's enough for implementing sessions in my opinion.
A token is generated on a successful login of a user, stored in the database and passed to the user. Whenever a user accesses the webpage, the token is passen in the request and is validated. If valid the last_acces_timestamp is refreshed and the user can proceed. The lookup in the validation will be done by token and with the username you can do the authentication and authorizaton. If token is invalid or expired, forward the user to a login page.
Deleting expired sessions out of the db can be done periodically using a cron job or on creation of a new session.
For performance reason you might think about storing the session in a hashmap in memory. Since it might be costly to always update the database.
Also think about using HTTPS, to prevent people sniffing the token.
I have solved this the following way, few months ago:
https://stackoverflow.com/questions/12829994/java-custom-session-implementation-expired-sessions
Usage of UUID is not recommended according to RFC 4122 it is stated that
Do not assume that UUIDs are hard to guess; they should not be used as
security capabilities.
I would recommend combining and multiply all of the following information together into a hash with also encrypting it using a public key stored in your server.
UserId (Or User UUID that was generated for each user while registration)
Encrypted His/her password (considered as a private key for encryption per each user)
Time stamp
Client Operating System
Client User Agent (Browser name)
For storing tokens, you could use either memcache which is used heavily in big companies or redis if you are focusing on persistence.
Keep sure that your cookies have the following attributes, for more info about Cookies
HTTP Only cookie
Secure Cookie

php How does a "stay logged in" checkbox influence logging process?

I'm wondering what is happenning when a user logs in a website with or without checking a "stay logged in" checkbox.
From what I understand start_session creates a variable on the server and stores the session id on the client's browser in a cookie, destroyed when closing the said browser.
Following that reasoning, I guess that checking the "stay logged in" checkbox pushes back the expiration date by N seconds, which would be achieved by setting:
setcookie(session_name(), session_id(), time()+N);
In that case, I see no need to use cookies, at least for the logging process.
Am I right or awfully wrong? :)
There are a couple of typical ways a "stay logged in" box might be handled...
You could set the user's info in a persistent cookie, and look for that cookie.
You could create some unique ID (it better be a big one...like a GUID or a hash), store it in a persistent cookie, and let that serve as auth info.
Note that most solutions don't involve the user actually staying logged in; they just make it so that the user doesn't see another password prompt. Keeping the session alive and storing the session ID in a persistent cookie is technically possible, but for a site with a lot of users, that'd be quite a bit of extra space and load on the server.
The way I have written an option like that is to create a token and store it in the database along with the user ID. I then give that token to the browser as a cookie. Anytime a page request is done, I check to see first if the user has an active session, then if they have this token cookie. If they have a token cookie, I look into the database to see if it is valid and if so create a session with that user ID.
I'm sure this is an insecure, easily breached method, however.

Secure user authentication over https with php and sql

Currently, my authentication looks something like this:
Login over SSL:
PHP checks username / password against database.
If they match, a session cookie is generated, sent and stored on the Db
(A session cookie looks like userID:IP:random_characters:timestamp)
Perform action over SSL:
PHP checks cookie against session database.
If the session matches, is under an hour old, and the user id & IP matches it is considered valid.
If the session is valid the action is performed and a new session cookie is generated.
(If the action is a logout, the session cookie is set with an expired time)
If a cookie is hijacked and the IP is spoofed within an hour of the victims previous action, the attacker will be validated and a new session will be generated. Are there other possible issues I'm overlooking?
What's considered the best practices? I'd like to tighten up the security. Thanks!
You could tie the session not only to the IP but also to the user agent. Then a not-so-smart attacker has another obstacle.. not a big one though as he probably has a way to steal more session cookies if he could steal one and then just try various useragents. However, to make it not as easy as changing the useragent and retrying with the same cookie, delete the session to require a new login (using username/password).
Last but not least, there is CSRF: If the user is logged in an external site could make him perform an action. As the user's cookie is valid this action will be performed. You can only circumvent that by passing random tokens via URL. An easy way which even allows browsing with multiple tabs would be using a (random/changing) part of the session ID for that. Random tokens only valid for a single request would be even more secure but they are extremely annoying as they prevent people from using multiple tabs (even though the app could simply store e.g. up to 10 tokens and only invalidate old tokens if there would be more than 10 - that would allow you to use not just the most recent token but also the one from the page you've been on before opening a new tab). But most likely a token valid for the whole session is sufficient; assuming you use the session id or a part of it you have a bigger problem than CSRF if someone can get the session id of other people
Regarding logout, I would not only use an expired time but also an empty value - so if something goes wrong (I've seen both servers and users with horribly incorrect (days+) clocks).

Categories