laravel and multi-sessions from the same browser - php

In our web app, If I use a single browser, login to our application as user A, open another tab and login as user B - User A loses his session data. I assume this is due to a shared cookie made out with the user-agent. Is there a way to concat its name with a username? so that sessions can co-exist between concurrent logged in users using the same browser on the same machine?
We use Laravel 5. Is there any way around it?

Laravel Session Background
Sessions
Skip this section for a quick easy solution
In Laravel, session cookies are created via the Illuminate\Session\SessionManager class, namely through the buildSession method:
SessionManager::buildSession
protected function buildSession($handler)
{
if ($this->app['config']['session.encrypt']) {
return new EncryptedStore(
$this->app['config']['session.cookie'], $handler, $this->app['encrypter']
);
} else {
return new Store($this->app['config']['session.cookie'], $handler);
}
}
In this method we can clearly see that the name of the session comes from our config\session.php, looking in particular this line:
session.php
'cookie' => 'laravel_session', # ~~ ln 121 at time of writing
Ok, but that doesn't help a lot, changing this, changes it everywhere, as noted by the comment proceeding it in the config.
The name specified here will get used every time a new session cookie
is created by the framework for every driver.
And even if we could pass it some dynamic value, something like:
'cookie' => 'laravel_session' . user()->id,
This creates a paradoxical, time ending, universe imploding outcome because you are requesting the id from the user which is accessed via the session looked up by the cookie name laravel_session.. (mindblown)
Let's leave SessionManager and it's session.php configuration alone. We can see from above that regardless of how we approach this, all our session info will be fall under that single laravel_session key.
Guard
Maybe Guard will have some more information.
Guard is your key to auth into your app, and one of the many things that makes Laravel awesome for quickly creating applications.
The method to look at is Guard::user().
One of the first things Guard::user() does after some initial cache and logged out checking, is a session check.
Guard::user()
$id = $this->session->get($this->getName());
So here, Laravel is fetching the session values that match the result of getName() - awesome - all we need to do is mod getName() to return a value, let's take a took at that method:
Guard::getName()
public function getName()
{
return 'login_'.md5(get_class($this));
}
That's pretty straight forward. $this refers to the Guard class, so the md5 will effectively always be the same (if anyone knows the 'why' behind md5'ing the class name which would be the same each time, leave a comment).
There are a few places where this should be updated, such as getRecallerName.
So from here, you can extend the core Guard class and splice in your getName and getRecallerName methods.
You will probably want to wrap some service provider around this, write some unit tests, possibly even overwrite the original auth manager.
"Geez, that seems like a lot of work"
"It sure is Billy, it sure is"
https://www.youtube.com/watch?v=dTxQ9yhGnAg
See the next part
The quick "I just need an answer" answer
Ollie Read has already created a solution, found here:
https://github.com/ollieread/multiauth
I encourage you to have a look, especially the custom Guard class which extends core Guard with custom getName methods.

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.

Multi userlogin with same browser like google add account. for that you need follow some steps and re-write auth library which provided by the Laravel,
Steps
Tack backup of your Auth file.
Change all session store functionality to store it first in array and then store that array to session
Now you need to create the new session variable which will store the current user instance id like user 0 1 2 ...
Now you need to change all the function from you will get the values from the session you need to check if the session object is empty then user is logout else you need to get data of the user base on the user instance.
You need to change your instance when user want to switch from one account to another.

The easiest is just a URL based sessionID which could be a security issue depending on how your application is designed, especially when sharing urls with non-expired sessions.
Since L5 doesn't support php native sessions anymore, you'll have to use a custom provider like below:
This will use sessionID in the url for laravel V5:
https://github.com/iMi-digital/laravel-transsid
Basically the session is URL based, so you can just login in a different tab and get a new sessionID, and that person can easily do a "open page in new tab" to have two pages of the same user if needed as well.
The library above locks the session to the IP and User Agent so link sharing won't accidentally leak a session.

tl;dr: Yagni
Consider a person (http client in your case) with 2 identities: Dr Jekyll and Mr Hyde.
He visits his new friend Sir RM1970 (http server in your case): "How do you do, RM1970!".
Here is the problem. Poor RM1970 need to welcome back the monster, and there are few options:
fall deep into this rabbit hole: "How do you do both Dr Jekyll and Mr Hyde!", which incredibly complicates further conversation (your ACl, for example, will need to operate with list of identities, and make a decision about priorities if they conflict realtime)
make a decision on your own: "How do you do Dr Jekyll!" and pray you made the right choice (randomly pick user's identity and bring your users some fun with unpredictable responses)
be sly and shift this responsibility back to him: "Pardon me? Who are you? Name yourself!" (require single identity per request)
The later is how it actually works. The browser provides the latest confirmed identity.
You've been asked to change this, but do you really want it? Hold the line and don't accept this responsibility.
If you are not going with first 2 dead-end options, you will need to ask user on which behalf he sends the request. The best option here is to make your frontend stateful, maintain list of opened sessions, and provide a UI for user to pick one. It is almost the 3rd Ryan Bemrose's option, but store this data on client side, and send only the chosen one. No changes in laravel backend required.
The problem here is switching tabs will not automatically switch user, and will be rather confusing, providing very little difference with logout/login path, which is already implemented.
Some browsers support multiple profiles (example), which may be an acceptable alternative. Basically it is the same as 1st Ryan Bemrose's option, but does not require multiple browsers installed, and can benefit from permanent cookies, aka 'remember-me'.

I don't exactly know what do you need this for, but as a developer I sometimes have to log into an application with multiple users. To do that I usually use incognito mode or if its more than 2 users I had some luck using this extension in chrome.
I know its not an answer to your question but it just might be what your looking for.

Different seesions coexist between concurrent logged in users cannot just only implemented by session cookie,because cookie is stored by browser. So the logged
in user's seesion must be stored by Server.
As all we know, Once session_start is called,SessionID is created and then temp file is created in server's temporary
directory.
Diffent user has different SessionID and after session_destory called then all SessionIDs stored in Server and Cookies are recovered. You can rewrite this behavior by implementing SessionHandlerInterface. Of cause many web framework support this,Laravel has not exception.
Here is the document:
custom-session-drivers

I don't know how complicate it is to code it into laravel but this could be one solution:
You use a different session name, has to be a string, and code it into the url every time so the application knows which user made a request. So you can call the session variables by a normal name.
<?php
if(isset($_GET['id']) && !empty($_GET['id']))
session_name($_GET['id']);
session_start();
if(isset($_GET['user'])) {
$_SESSION['user'] = $_GET['user'];
}
if(!empty($_SESSION['user']))
echo "Hello ".$_SESSION['user'];

Related

Use URL Query String to Authenticate in PHP

Normally When I design a site that allow users to login, I create session variables of the user info from the database and ensure that at least one of the session variable is available on each page of the site, else the user would be redirected to the login page like this:
if(!isset($_SESSION['username']))
{
header("Location:login.php");
}
But then I've been thinking lately, instead of using session variables to authenticate users, why not use the query string. My idea is to create a unique string which is based on some factors, like date(month, year, day) or access time(day, hour) or ip address, and maybe hash it using md5 so the url might look like this
://mysite.com/dashboard?auth=12jsdnnau819wiskj3jdnck23ksj12j3.
So now I can easily logout a user if he has not accessed the site for more than one hour or more than one day. But I do not know if this is a good idea, that is why i am here, to seek for advice on a better way to go. Thanks all.
Because URLs:
Leak easily
Get bookmarked
Don't carry their data over automatically when the user opens a new tab and navigates back to the site
and because nothing is stopping you from storing the same data in a session and using that to easily logout a user so it doesn't even add the one benefit you highlight.
Don't do this.
Do you remember the days when PHP session ID could be stored in the URL, and you would have URLs that look like: index.php?PHPSESSID=.... ?
We have moved away from this to more secure implementations, user friendly URLs, etc.
Just as a simple example of what can go wrong: A web crawler can crawl your website, and if reaching the admin panel with a properly authenticated URL (as you described), it could become publicly available to ... anyone.
So ... don't reinvent the wheel.
I can think of a few reasons not to do this:
The hashed request parameter is publicly visible. This means anyone using that url will be considered an authenticated user.
As I can see, the token is per user, so all requests using this token will be done on behalf the same user. If you share a url, anyone using that url will impersonate the same user, and have all the access rights granted to that user.
Other answers have mentioned that the urls can easly leak, or be bookmarked. In addition to their points, once the token expires, the url may become broken, if you trigger a login or other authentication redirect mechanism.
A pure technical drawback of your idea is also this: you need to persist the token when navigating across different pages in your app
The HTTP Session has been designed to serve this purpose and at the same time be safe enough. The application session length can be tuned so that it matches your requirement. I recommend you to get familiar on how HTTP sessions work and how to tune your session expiration policy, rather than compromise your application's security

How do I properly separate environment using sessions?

I got a situation here in my software, that can bring me a lot of headaches. I'll try to explain the best I can.
I have 3 environment actually, all 3 environment use the same register, log in and recovery system. But each one of them are properly separated, let say the structure is:
Admin/
Store/
Supplier/
The structure above contains a Register, Log In and Recovery methods in a class but the code is the same. I know it would be made all together and create some identification code to separate them, but the software was not planed to grow like this, and now we got a situation. ;(
I first got the problem when I was logged into Admin account for instance, and try to access the Store/ environment (account) without any session (but of course the session used was the admin account). I got the access and it is not safe if one supplier know about the problem or customers of the Store know about this.
What should I do to prevent this type of problem? Re-Write the code? or are there some workaround that I can write into my session code?
Thanks!
You can use session_name() to define which cookie name is used to store the session ID, this way you can name and separate the different environments under the same Host. Use it before calling session_start().
For instance, every session_start() call in the Admin area should preceed with:
session_name("ADMINSESSID");
session_start();
And you do similar things to Store and Supplier.
Although it works to separate $_SESSION environments, I'm not totally sure about the security aspect of it (for instance editing Cookies to use the Supplier session ID as an Admin session ID). Perhaps you should also mark in each $_SESSION which environment it belong to.
At login time:
// if user authenticated successfully to that environemnt
$_SESSION['environment'] = session_name();
Anywhere else in the same site area:
if ($_SESSION['environment'] != session_name())
die('Access violation.');

Multiple Domain Single Sign On

I'm currently trying to join 2 web apps on different domains example1.com and ex.example2.net so that you can login to example1 and click on a link to example2 and be instantly signed in, as it would be more convenient for customers to just login the once and navigate between the sites.
I've researched various means ie. php sessions, openID, JOSSO and Kerberos, but what would be a secure and easy way to implement this?
Kerbros is very extensive and for systems requiring high security. Its very difficult to work with, and even just setup over all, I would not suggest this route unless you know linux very well, and provided your hosting provider allows you that type of access over the machine.
Im not familiar enough with JOSSO or openID to comment well on those however.
Any php sessions are only good for the domain, and server the domain is on, kind of like cookies but not, though in all you would use them.
I think your easiest solution more so if the 2 sites are on the same hosting account/server. Is to have a database specific to this cause. What you would do is create a login system like you would normally but instead you would have 2 sites reading off this login. Aside from the normal login you would also have a session tracking table. Typically you already set sessions when users login to keep them logged in, and you'd do the same here, but you'd add a cookie into the equation one both sites can recognize and use to compare entries in this new table where your tracking your users. I'd say keep try by IP, Browser, and maybe a userID all in one cookie with a unique hash of some kind as well thats specific to the user based on something only the servers could recreate on the info they have for the user.
Of course I dumb it down in conceptual speak, its a little more elaborate than I make it out to be, but this would be your general stepping stones.
Also if the sites are independent of one another you could always create an API between them to pass info back and forth JSONP style so one can act as the hub for the login while the other just validates
But in all its all dependent of what your wanting to do overall how, when where, etc..
You can have one application handle logins for both sites using php sessions.
example1.com
user logs in and php session cookie is stored.
ex.example2.net
check example1.com and validate session cookie. if it does not exist redirect to example1.com login page or a custom login page on example1.com. If it does exist, then log the user into ex.example2.net.
If you only want a link then you generate a hash and pass that to the second app once they have logged onto the first. If the hash validates, then log them in.
If it works for you, my suggestion would be to go the openID route. It's the easier way and it's secure enough. Besides, the registration process is easier and quicker to users too.
You can actually only allow IDs from your sign-in domains, if you prefer, making it pretty much a "private" login system.
There are downsides too... You don't have fine control over the registration process, you are dependent of openID authorization process... There are some problems that might happen if your host is not well configured (timezone differences, for instance).
But overall, it's a relatively secure system, easy to implement.
Kerberus is extremely secure but it's a nightmare to work with. Unless you're dealing with highly sensitive user information, like credit card numbers, or think your websites make apetizing targets for hacking I don't think it's worth your time.
I would use a database table that is shared between the two sites. If you go down the PHP session route don't try and just pass the session data from one site to the next on separate domains, it won't work. I found this post helpful many moons ago: Single Sign On across multiple domains

How to destroy a specific PHP session

I am looking for insights into how to destroy a specific session in PHP. Through a partner website a user logs into the main website using a token and obtains a full session.
It is also possible for the partner website to call a destroy function if the user logouts from the partner website. We should then also log out our own user.
What is the best approach to this? The Zend_Session destroy method does not accept a parameter, similarly the PHP function session_destroy does neither.
I am considering two options:
Removing the session information directly from file/memcache but would prefer a "cleaner" approach than this.
Checking at every page request if this is a "token" user ; and if then check if their token was expired by maintaining a list. This adds overhead to a busy website, but might be my only option.
Or is there a third / better approach I am not seeing?
There's no need to roll-your-own session handling.
session_id() can take a parameter, the session id you want to work with.
So, when you pass the user off to the partner site, pass along their session_id (or some token, or whatever).
Then allow the partner site to hit a script like this:
kill-user-session.php
<?php
/**
* Destroy any active session identified by $_POST['sid']
*/
session_id($_POST['sid']);
session_start(); //this line may not even be necessary
session_destroy(); //destroys that session.
So when the user logs out on the partner site, the partner site POSTs the session_id (that you gave them) to your kill-user-session script, and the user's session is destroyed on your server.
Of course, you probably want to limit access to kill-user-session.php via some method or another.
If you wish to be able to 'kick' the sessions of a user(s), the only way you can do it is if you use MySQL (or someother db, sqlite even) for your session storage.
Then you can simply remove entries from the db to kill a session.
This also allows you do do things such as, 'take control' of a specific user's session and other stuff :)
See this for a very basic run through: http://www.devshed.com/c/a/MySQL/Custom-Session-Management-Using-PHP-and-MySQL/ (not the best example but good enough full example to start you).
EDIT
Also, if logging out through the partner site, another method I have used in the past (which was with O2 and other such sites) they were given a 'callback' (REST API call in most cases) which they would also need to call when the user logs out of their site.
The database solution means that the session database needs to be shared between mainwebsite and the partner site, which frequently isn't the case etc. Maybe something along these trivial lines would suffice?
<img src="mainwebsite/logout.php">
mainwebsite/logout.php:
<?php session_destroy(); ?>

Letting users try your web app before sign-up: sessions or temp db?

I've seen a few instances now where web applications are letting try them out without you having to sign-up (though to save you need to of course).
example: try at http://minutedock.com/
I'm wondering about doing this for my own web app and the fundamental question is whether to store their info into sessions or into a temp user table?
The temp user table would allow logging and potentially be less of a hit on the server, correct?
Is there a best practice here?
It should work exactly the same way the application usually works, with the only difference being that a flag like thisIsATrialUser is set. You shouldn't create two different ways to do things internally.
Create a class of user, lets call it your Anonymous User Type. Give all unauthenticated users anonymous accounts (you have to clean up old accounts at some point). Use a persistent cookie to associate old users with their anonymous account. Make them authenticate themselves whenever they need to perform something that requires payment or full registration. Change their user type to something like Regular User Type once they are authenticated so you can keep all the information that was already attached to them when they where anonymous.
This allows tracking and storing of potential information like shopping carts without requiring registration upfront. Your code shouldn't have to change much if you treat anonymous user similarly to regular users. Otherwise you have to create an entirely new set of code to manage special users that are not stored in your master user table.
To clean up the data added by trial users, you can create a script to delete all the data that was created lifetime of cookie + 1 day and owned by any trial user. You can auto-pilot the script with nightly cron.

Categories