Chrome: "continue where I left off" mode and Symfony2 - php

Using Symfony2 and FOSUserBundle, I am not getting the expected behavior on the following implementation.
To begin, know that Continue where I left off option in Chrome restores completely the user session independently of having checked some "Remember me" or something like that. Therefor,it saves a cookie with all the information of the session.
What I am trying to do is to avoid the creation of a session from the cookie stored through that Continue where I left off option on Chrome.
Or, if I cannot avoid the creation of the session, at least try to know that the session comes from that completely transparent way.
I have found this in Symfony2 documentation (specifically here):
In some cases, however, you may want to force the user to actually re-authenticate before accessing certain resources. For example, you might allow "remember me" users to see basic account information, but then require them to actually re-authenticate before modifying that information.
The security component provides an easy way to do this. In addition to roles explicitly assigned to them, users are automatically given one of the following roles depending on how they are authenticated:
IS_AUTHENTICATED_ANONYMOUSLY - automatically assigned to a user who is in a firewall protected part of the site but who has not actually logged in. This is only possible if anonymous access has been allowed.
IS_AUTHENTICATED_REMEMBERED - automatically assigned to a user who was authenticated via a remember me cookie.
IS_AUTHENTICATED_FULLY - automatically assigned to a user that has provided their login details during the current session.
So, if I don't get it wrong, a user that transparently logs in as a result of the Copntinue where I left off option, should have the IS_AUTHENTICATED_REMEMBERED.
Well, the reality is that it is not thus. The reality is that the granting is IS_AUTHENTICATED_FULLY.
Has anyone passed through this?
Any idea on all this?
Thanks

Sessions are handled server side. Depending on your server's configuration for sessions lifetime, you can close your browser and re-open it without losing the session. This has nothing to do with the Continue where I left off option of Google Chrome.
The granting is IS_AUTHENTICATED_FULLY because the session is still active on the server and not because of the Google Chrome option.
Simple example use case. Let's say we set a 5 minutes session lifetime.
Without the remember me option :
I log in : a session is created on the server.
I close the browser.
I come back 10 minutes later : session has expired therefore I have to provide my credentials.
With the remember option :
I log in : a session is created on the server AND a cookie is created on my browser saying hey I'm connected.
I close the browser.
I come back 10 minutes later : session has expired BUT as the result of the cookie saying hey I'm connected, a new session will be automatically created. Therefore I will not have to provide my credentials again.
In both case if you come back within the first 5 minutes you will be automatically logged in because the server still handle a session for your browser.

Related

Can't login with Symfony Guard, maybe because of cookies & multidomain

I need a very simple form login, so I'm using a Symfony Guard authenticator for this.
My application is multi-domain, for now login is possible on only 3 of them (and 2 are subdomains). But I can't log in on any of them.
First, here are some resources:
The authenticator: FormLoginAuthenticator.php
My security configuration: [https://gist.github.com/Pierstoval/1e29a9badab1cba03e45a306aa658c83#file-security-yaml)
And in case it's needed, the session configuration in the framework: config/packages/framework.yaml
I don't understand why, but to debug this, I tried updating config/packages/dev/web_profiler.yaml to set intercept_redirects: true, to debug cookies and security config, and here's what I have when reproducing the common login scenario:
When accessing the backend page (that shows the login form) I have the session cookie with a value like llno5smlmsema5nq02kr30s6m2.
After I submit the form with correct values, I see the profiler's redirection page, and the Web Debug Toolbar shows me that I'm Logged in as Pierstoval and the session cookie sent by the response changes id and becomes b1vbcnvp7e6p0j0o0vtd7ui7bf (because by default the Firewall migrates the session when logging in), so this works. I can even see that the token is in the session. I also added dump() statements in the Session::save() method, and dumping session content shows the token. And the token is in the session file when I dump it too (in var/sessions/dev/sess_b1vbcnvp7e6p0j0o0vtd7ui7bf).
I click on the http://back.esteren.docker/fr/ link showed by the redirection page, and then I see the session cookie id did not change (it's the one that have been set after logging in), but this time, the Web Debug Toolbar shows me that I'm Logged in as anon..
To me, there's a problem when the session is saved, so I took a look at the session files in var/sessions/dev/, after the redirection.
When I cat var/sessions/dev/sess_b1vbcnvp7e6p0j0o0vtd7ui7bf, I see this:
_sf2_attributes|a:2:{s:18:"_csrf/authenticate";s:43:"bCm23vBGyrq52caehAH9cCTjJkAmdT9j9Z8yygaET78";s:26:"_security.main.target_path";s:30:"http://back.esteren.docker/fr/";}_sf2_meta|a:3:{s:1:"u";i:1538092899;s:1:"c";i:1538092750;s:1:"l";s:5:"86400";}
Which, when unserializing the session attribute bag array, gives this:
array:2 [
"_csrf/authenticate" => "bCm23vBGyrq52caehAH9cCTjJkAmdT9j9Z8yygaET78"
"_security.main.target_path" => "http://back.esteren.docker/fr/"
]
This means the token has not been saved in the session after the successful login.
And even worse: since I already debugged the session file before redirecting, I know that it was saved ni the session and at some time it was removed from it before the session being saved another time.
How is this even possible? Did I forget something in my security configuration?
As a side-note, I tested with and without docker (with a plain php-fpm+nginx setup) and none worked, this probably means that it's not a permission issue from the docker side...
Finally, thanks to #Lynn and #Wirone on the public Symfony Slack channels, I ended up with a solution.
First, the bug was the fact that my User entity (which is serialized in the session) did not store the password.
And when a token is created, it checks if the "old" (in session) user password is the same as the "new" (refreshed) one, thanks to the Abstractoken::hasUserChanged() method.
And if the user object has changed, the firewall logs the user out.
To fix this, either serialize the password with the user, or implement EquatableInterface and use your own checks.
And this is what I did:
Implement EquatableInterface
Serialize only id and the timestampable fields createdAt and updatedAt
Make isEqualTo() check class and date fields to know if the user has changed.
Hope this helps 👍

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).

FOSUserBundle - Unique session access

Using Symfony 2.0 and FOSUserBundle, I need to know how to restrict the access to make it unique.
This is what I mean:
User X accesses to my system creating a session through login/password
With that session still valid (not having closed the session, etc...), the same user X tries to access from a different computer or location.
In that case, I need the system to avoid its second access with some kind of message: "that user has a valid session from another computer".
Is that possible?
It would be possible and trustable only if you could find a secure way to know when the user session has destroyed or he has logged out from the other computer. As it can occurs without explicit action from the user (i.e. he closed the browser and the session timed out), I wouldn't rely on it. Of course you could always try to find some workaround (i.e. predicate session expiration time and track user logging out) but it still would not be 100% secure. Thinks about cases where new accesses will be denied because a session is still open on another browser without people in front of it.
On the other hand, you can do it the other way (when new user logs in, the other logs out) using Voters and some hints found in Allow one session only at a time.
Correct me if I am wrong, but I think there is finally no way to do what I am trying to since Chrome (and I think FF too) save the exact cookie and are able to restore the session skiping all LoginHandler methods.
Let me explain myself.
Right, I was about (and actually I did) to implement the solution described in your answers and comments:
User X enters the web site with his login/password using Safari (for example)
The login datetime is stored both in table User in the database and in session
Without logging out, the same User X opens a different browser (Chrome, for example)
The new login datetime is updated in database and in Firefox session
The user gets back to Safari and tries to refresh the page
He gets an exception as the datetime doesn't coincide with the one stored in session
Well... great so far, as it seems to solve the problem.
And here comes the big deal: as described here and here, Chrome is not deleting properly the session cookies. So when user doesn't logs out and just close the browser, anytime he or she comes back to Chrome, the session is automatically restored without passing through a login handler, login method or anything around.
This causes that "magical" datetime key not to be saved both in database and session and, as a result, put a stick in the wheel of letting just one session as a time, what was the original plan.
Any more light on the issue??
I want to cry :(

Session survives browser close? Should I want to prevent this?

I have a PHP app which requires log in, offers a log out option and force logs off users who have been inactive for X minutes.
But, if I log in, close my browser and re-open it, the $_SESSION variables still exists.
What's the general practise here? Should I want to prevent this and, if so, how?
Something in me just wants to treat closing the browser as logout ... on the one hand, it's a secure app (since it requires login) but a non-tech user might reasonably expect that if they close the whole browser then no one can see their private data. Otoh, if the browser crashes and the user restarts it, he might hope to pick up where he left off ...
What do others do?
PHP sessions work by saving a cookie to the user's browser containing the ID of the session on the server. Therefore PHP sessions work exactly like ordinary cookies do.
If you close your browser, cookies are persistent. The server doesn't know what instance of the browser the user is using, whether the browser has restarted, or even if the computer has restarted.
Providing a log-out button is the most usual practice here, but if for some reason you require the user to be logged out when the browser closes, you will have to implement something client-side, as the browser doesn't send any signal to the server when it closes.
If you are concerned about security - i.e. you are programming a highly secure application such as a payment gateway - you can follow the practice of bank websites or other payment gateways;
When the user returns to the site, they are still logged on, but when they try to perform any action that will affect the logged-in user, re-authenticate with another password screen, or ask for some memorable information.
This is a classic behavior, you can observe it on many sites, including Stack Overflow :)
Your session variable is bound to a cookie in the browser. If you want the user to really be logged off when the browser closes, sets the time of the session cookie to zero.
When you explicitly set a cookie, you can choose its expire time. When you're using session_start() to generate a session cookie, its expiration time is determined by the session.cookie_lifetime value in php.ini. If you set this to 0, session cookies will expire when the browser window is closed.

Session should never expire by itself

I'm using login function in my site with session.
This session of mine gets expired after a few minutes irrespective of whether the user has logged out or not.
Now what I want is that the session should only get expired when a user logs out. If a user doesn't log out his account and then comes back after 2-3 days, even then he should appear logged in.
I have found some examples where they have increased the time for a session to expire but I want that it should only expire on the log out event by the user irrespective of the time he took to log out.
How can I do that?
In particular, is this the right way to do so?
session_cache_expire(0);
session_start();
A solution that is often used, in this situation, is to:
have a not-too-long session duration: it will expire if the user is not active (that's just the way it works -- and that's better for your server if you have lots of users)
when user logs in, you set a cookie that contains what is needed for him to be recognized
if he comes back on the site (with the cookie, and without having an active session), you use the informations contained in that cookie to auto-log him in, re-creating the session at the same time.
This way:
you don't have thousands of sessions "active" with no good reason
you keep the standard way sessions work
And you have the advantage of "never being logged out", at least from the user's point of view.
Also note that with "normal" sessions, the cookie containing the session id will be deleted when the user closes his browser -- so, he will be disconnected, no matter how long the session's lifetime is.
With the solution I propose, you are the one who sets up how long the cookie should remain on the user's computer ;-)
It means, though, that when a user manually logs-out, you have to delete both his session and the cookie, of course -- so he's not immediatly re-auto-logged-in.
Of course, you have to be careful about what you set in the cookie: a cookie is not quite secure, so don't store a password in it, for instance ;-)
Actually, this way of doing things is how the "remember me" feature often works; except, here, your users will not have to check a checkbox to activate "remember me" ;-)
If you don't have the time to develop that kind of stuff, a pretty quick and dirty way is to use some Ajax request on all your pages, that will just "ping" a PHP page on the server -- this will keep the session active (but it's not quite a good way of doing things: you'll still have LOTS of sessions on the server, you'll have lots of useless requests... and it will only work as long as the user doesn't close his browser).
You can't do that with the PHP internal session handling alone. PHP will always send out the session id in a session-cookie which will expire when the user closes his browser. To achieve some sort of auto-login you'll need some accompanying code that sets a longer-lasting cookie on the user's browser and handles the recognition of these cookies and the mapping between the cookies value and the respective user account.
Please note that this greatly affects security issues so you'll have to take care of a lot of things. Please read the following on how a possible auto-login feature could be working:
Persistent Login Cookie Best Practice
Improved Persistent Login Cookie Best Practice
Do you remove your cookies while testing? Are cookies enabled? Do you destory the session somewhere in your code?
Also, see my answer to another post: Quick question about sessions in PHP which explains how to stay signed in. Just don't do a cronjob/sheduled task if you want the user to stay logged in forever.

Categories