I'm developping a website with a member area and user levels. admin members can access to an area where they can view, add and change some sensitive data.
To add a protection layer on this group of pages, i'd like to ask users their login and password before accesing the home page of the restricted area even if they are already logged in.
How can i do that ? in the symfony doc, i haven't found anything about a second authentication of the users.
What you're looking for here is IS_AUTHENTICATED_REMEMBERED vs IS_AUTHENTICATED_FULLY. If you have remember me functionality you can set the session to expire after 30 minutes to an hour (or whatever time interval you think is ok) When the session times out, in admin areas, if you check for is authenticated fully they'll be asked to login, if they visit any place with lower bar (e.g. is authenticated remembered) they'll still appear logged in. This is basically what amazon does when you try access your user settings page. Sure you're logged in and can add stuff to your cart, but once you try and access your sensitive data, you must re-login.
I would avoid a second authentication... I is better to work with restriction layers (user roles)... the FOSUserBundle already provides all the needed functionality
Related
Current app:
The app I'm working on has a single User entity.
Users can be "member" or "manager" using a boolean attribute stored in database (is_manager).
All the users connect trough the same routes (/login) and are redirected depending on the attribute is_manager.
Here is my problem:
The client want to be logged as a "member" in one tab and an other user "manager" in an other tab of the same browser.
Not several "members"/"managers" at the same time in the same browser.
How can I acheive it with Symfony 3.3 ?
Based on my idea the session ID should depend on the attribute is_manager but I'm not really sure of it and don't know how to do it with Symfony.
Any help will be really appreciated!
You could use User switching to almost solve this problem. Instead of logging in as member or manager, they login as a user with the role ROLE_ALLOWED_TO_SWITCH. You can then provide links to the page they want to see with an added attribute ?_switch_user=member_user or ?_switch_user=manager_user. Both those users have to exist and need the correct permissions.
This may not be perfect, for example you have to maintain 3 different users for 1 account and you have to make sure they don't accidentally perform actions after switching the role, but that is the best way I can think of, to support this kind of switching.
You need to tune up your user management system. You can put some vars in your session saying to your system to behave un some way (manger or user) but if it must depend on the open tab, then the best approach that I can think of, is using the url. Put some kind of token (secured one, of course) on the url that tells your system how to deal with that user.
On this approach, if the user closes the tab, it becomes automatically logged out, at least in one of it's roles.
So you can do a standard login process, set the session for the manager role (the more privileged one) and the build and present a link to the user to he non manager versión. That url contains the mentioned token, and if he closes the tab and wants to recover the tab, it should go to the manager versión and follow the link again.
I'm looking for a way to limit all routes within a group to one user at a time. In practice, this translates to an admin control panel that only one admin can use at a time. Any number can be logged into the web app at any given point, but if one enters the control panel, it should lock the others out until he returns to the regular client website.
I'm running laravel 5.2.9
If you asked and forced all users to Logout after leaving the Admin Panel
Then it will be so easy:
After login add a flag in Database
Then add a middleware to Route::group when flag exists don't allow others
After logout, remove the flag
Once you want to let all users login to the system, so limiting the session timeout is not the solution.
You can do as following:
Store last user and the access timestamp to larval Cache or DB each time a user visits admin page
Before allow a user to enter admin page, check the stored user and its last access from Cache/DB. If no data was stored (first access) or it was expired (max idle time after user's last access) or it was the same user, then let him in.
This way if current admin stay idle and someone else took the page, then current admin will be redirected to normal area on his next action.
I have a problem in my project. When admin is logged in, no front end user can login in the same browser, why this happens? But when I destroy the cookies and then tries to login as user it correctly logs in.
How can I solve this?
Any major browser will only store one session cookie for a site, but the site developer gets to choose what's in that cookie. It seems like your site is storing user information in the session cookie, which is then getting overwritten when the other tab stores different information in the same cookie.
You don't provide much detail about how your specific site operates, but here are a few general ways of approaching this problem.
1) Use different browsers for different users. Different browsers don't share cookies between them. If your goal is simply to test your site with multiple users, this is the way. You can also use Incognito/Private mode to log in a separate user, as this mode doesn't share cookies either.
2) Don't use session cookies to store user information. This is a non-starter on most websites, but if this is an internal site or strictly controlled environment, you may be able to pass user identification via the URL, POST data, or some other hidden identifier in the request.
3) Store data in the session cookie for all currently logged in users. Depending on the web framework, it may be possible to create a map of user -> cookieData and look up the correct one based on which user is making the request. This is an advanced technique, and I don't actually know if Laravel exposes this level of control.
Harshad is covering all the aspects very well, but I can tell about a little trick a I have used when I wanted to test using different user rights (same browser). In my case, it was Windows Authentication, but it does not matter:
1) define a flag at user level (e.g. SuperUser). It can be 0 (false) or 1 (true).
2) allow "impersonation" - if an administrator has SuperUser flag set, he/she can change its roles/rights and see the site as if he/she is a normal user with that particular rights, but user management section is still accessible, to allow changing rights back.
3) Little changes are required in the user management section to allow SuperUser security implementation (i.e. section is showing if user does not have admin role, but it is marked as SuperUser)
So, you are testing as a single user, no multiple session cookies or other tricks are required. You can have one tab opened with your user profile and other(s) to do the actual testing.
Note: regarding the multiple browser suggestion, it is a quick solution for developers, but in corporate environment, this can be a real problem, as users (e.g. key users that have to test security) do not have access to more than one browser.
I have written a system in which a background PHP process (written using the RabbitMQBundle) processes messages from a message queue. The worker process updates user accounts. This may include a change in the user roles.
The problem is that a user won't notice any changes in his roles while being logged in. The new roles only get applied after logging out and in again. From a security perspective this is not desirable. A user should loose any role as soon as an administrator takes away privileges from that user in the backend system.
The question is: How can a session for a specific user be updated with the new roles? Or when that is not possible, how can the session be invalidated?
Note that in the background process we don't have an active security.context or request that we can use. Code like this therefore doesn't work:
$this->get('security.context')->setToken(null);
$this->get('request')->getSession()->invalidate();
You can solve this in several ways:
1) via security.always_authenticate_before_granting
You can force Symfony to refresh user on each request, effectively reloading all roles with it.
See this SO question/answer:
Change the role of a distant user without having to relog
2) Via EquatableInterface:
You need to implement isEqualsTo(User) which in turn should compare everything with User class, including roles.
See this link: Create a User Class
3) Finally, you can use DBMS to store sessions. Then, it's just matter of find the record and delete it.
How should I design a login system so that each username can only be logged on in one place at a time? I want to keep users from giving their username to someone else to login so they can avoid paying for each user.
If a user is already logged in and tries to log in on another machine should I block the 2nd login (which could be a problem if the user was logged on at work and then tried to get on at home)? Or should I allow the 2nd login and end the 1st login? Or does anyone have a better suggestion?
Some Instant Messengers (that can work only with one logged in endpoint) have a nice way of sorting out such conflicts. They show a message like
You are already logged on from <COMPUTERNAME>
(in case of a web app, that would be <IP/Browser>)
and give you a choice between
either leaving that logon alive (and not log on from the machine you're on), or
ending the existing logon (and logging on on the current machine).
This is technically the most challenging, but definitely the most friendly way - it ensures a user has only one session running, without being too obvious about it. And there is no bad blood with users unable to log in because they forgot to log out at work, etc.
Blizzard's World of Warcraft I believe implements this beautifully.
Basically, if you try to sign into the game after already being signed in, the first connection is kicked off.
This basically just entails making the session stored on the database. When you store the session data, store a username too. When a user logs in, delete any session records with that users name, and then create a new one for the person logging in.
I wouldn't suggest blocking 'new' people trying to log in, because users don't want to have to go back to another computer they have (possibly miles away) just because they forgot to log out.
There are also some other things you might have to think of. Things like sessionid hijacking. If a user just puts a cookie on their system (which is always possible) with the right sessionid, it is possible that they could use the same session on multiple computers. In which case you'd probably want to keep an IP field where you keep the data on who is currently logged on.
A typical approach to this problem is to use an
inactivity time-out period.
This system enforces a maximum number of logins per account, while allowing for the situation mentioned: a user left the office without logging out, and attempts to login from his/her home workstation.
Here are the general lines of such a system
Each account is associated with a number of concurrent logins (aka "seats") allowed (it seems the OP wished one and only one, for every account, but this could be more, and vary on an account basis).
The license manager logic keeps a list of all accounts/users currently logged-in, along with a time stamp with their "last" activity.
Before serving any page, the web application, calls the license manager (LM). The purpose is to allow the LM to update the timestamp of "last" activity, but also to deny the call in case the license was taken (more on this below)
Upon each login, the license manager logic verifies that the number of seats taken doesn't exceed the amount specified for the account.
If that is not the case, the LM simply adds the current session to the list of active session
If that is the case, the LM check for sessions in the list which are older than the time-out period. If one is found, it disables it, and grants access to the new login. If none is found, the login is denied.
upon each [explicit] log-out, the LM removes the corresponding session from the lists of active session.
Note that the general principle outlined above can have some variations, in particular:
rather than silently and systematically invalidating the [typically oldest] timed-out session, one can inform the user currently attempting to logging about this situation and let him/her decide of the need to "kill" such a session.
To avoid burdening the LM with each and every new page request, the web application can keep track on a per-session basis of the time since the session was last "refreshed" in the LM, and only call the LM if such time exceed say 1/3 of the time-out period.
Independently from the LM logic per-se, remember to keep a log of all the LM-related events (logins, logouts, inactive session "kills", refused logins...). Such logs should include the date/time, the IP address and other relevant info, and are useful when resolving issues associated with stolen passwords and such. Such logs also contain invaluable marketing, for example to find all accounts which appear to have too few seats (and could therefore purchase some ugrade), or to find at-risk accounts etc.
A few more considerations
make it easy for users to log-out (log-out button/link on most every page, at a fixed location
make it easy for users to report conflict / stolen password situation
Block the first login. If you log in at home, then in work, you don't want to be blocked, since this is a legit method. Always allow the login in the present, and drop the old ones.
I would suggest keeping track of whether each user is logged in and allowing the second login to end the first login's session.
Then allow the user whose session has ended to report possible fraudulent activity if they were kicked off in error.
Don't try to do it by counting the number of IP addresses a user has an active session from - some users may be behind load balanced proxies.
The solution is to write your own session handler - probably easiest with a database back end - and only allow one user to have one open session.
You might want to tune the session garbage collection and inactivity. You should also ensure that your system is immune from session fixation attacks.
C.
In terms of security, and this is what you're getting at, it is always a good idea to store session data in a database anyhow. Particularly if you're on a shared server.
In terms of which user to allow and which to knock off that is a matter for you to judge. I suppose you could have some secondary form of identification to make sure they are the real owner of the account. The one who actually signed up to it.
I've done this before in a web application that had the same requirement. Here's what I did:
When someone logs in, you generate a GUID and store it in your database, attached to the user. You also store this same GUID in a session cookie.
Every time a logged in user hits any page on your site, you check their cookie GUID and compare it with the GUID that is assigned to them in your database. If these GUIDs don't match, they've logged in on another machine, and you log them out from that session.
This method works really well.