Laravel 5 forms && expired sessions - are there alternatives to laravel-caffeine? - php

So from my experience even in laravel 5.2 the following happens:
in the browser a page with a (ajax) form is shown
the user does something else (e.g. goes home)
when the user returns some time later and the session is expired, he gets an error message that the vrsf token is invalid (VerifyCsrfToken.php)
the user is confused
So far I did not find any build-in solutions for this problem. I am using https://github.com/GeneaLabs/laravel-caffeine but it feels a bit strange that I have to use a 3rd party lib to solve this basic issue. Maybe I missed something?

You don't have to use a 3rd party package for that. All it does is it add some JavaScript at the end of your page that pings the server via Ajax within a given interval in order to keep the session alive.
The CSRF-Token is part of a common security concept. But if you're sure you don't need that for your task, you can tell specific routes not to bother with it. In /app/Http/Middleware/VerifyCsrfToken.php you have an array where you can add the routes you want to exclude from CSRF verification:
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
//
];
But this might not be the best idea, because you're working around a security concept.
And this only prevents from the TokenMismatchException to be thrown. If the controller you're posting to still relies on an active Session, e. g. if it needs the logged in user or something, this won't help you either.
So, there are several solutions to this:
Turn off CSRF-verification for that route. Do this if you know what you're doing and if the session doesn't matter anyway
Hook into the \Illuminate\Session\TokenMismatchException and return a nicely formatted message to the user that their session has expired and ask them to reload the page (I guess that would be the recommended way)
Extend the session lifetime - as #TheFallen already stated, obviously not the best idea
Use that 3rd party package or just add that little piece of JavaScript on your own.

You can increase the time of the expiration of the session. Open config/session.php and change 'lifetime' to the time you wish to allow the session to be valid.
Alternatively in Exceptions/Handler.php listen for VerifyCsrfToken exception and show the user that he needs to refresh the page, login again, etc., which I think would be the better option, because increasing the session lifetime will increase the security risk.

Related

SimpleSAMLphp overwrites PHP (Zend) session, doesn't happen with older SimpleSAMLphp version

I had to update simplesamlphp on an old PHP server, the old version of the library was from 2010. Simplesamlphp is used as a Service Provider (SP) in a SP initiated enviroment.
I replaced it with the 09/'20 release and configured it the same. It's all working except one thing.
Simplesamlphp uses the PHPSESSION to store the session, by feature it replaces the php session with his and should set the old one again once the cleanup() method is called (on the session instance), after the authentication's complete.
This is not working, but I was fine with it because it didn't matter for the user.
Now I have to implement a button to test the SAML integration on a protected page.
By protected I mean it requires to be authenticated (through Zend Auth) to view the page, otherwise it automatically redirects (server side) the user to the homepage.
This is the code of the Action of this button (to test the SAML integration), that is inside this protected controller:
require_once('simplesaml/lib/_autoload.php');
$as = new SimpleSAML_Auth_Simple('cie');
$as->requireAuth(array(
'saml:idp' => $idp,
));
// --- user is redirected to the IDP and proceeds authenticating)...
$attributes = $as->getAttributes();
$session = \SimpleSAML\Session::getSessionFromRequest();
if($session){
$session->cleanup();
}
What happens is:
requireAuth() is called, my current session is put away and replaced with SimpleSAML's one.
user is redirected to the IDP and authenticates
IDP redirects the user back to my page
Zend does its things before my code is run (everything after requireAuth() is never run) and before the cleanup() method is called, so the old PHP session isn't restored
Zend checks the user isn't authenticated (because it's still using SimpleSAML's session) and redirects the user back to the homepage.
Said so, this doesn't happen with the old library from 2010, the old PHP session is never lost, I have no idea why. I checked everything my colleagues changed in the old library back in the day, but there isn't anything that deals with this.
Do anyone have any idea or tip I could follow?
Any workaround / idea to fix this issue?
I've been desperately googling stuff for weeks, but it's so hard to find something specific.
Thank you very much, just for reading this long question.
I managed to fix this issue very easily after many many hours, I'll write down what I did in case it may help someone else.
My problems were:
simplesamlphp using the same name for the session cookie as my application (I previously already tried changing this setting, but because of the second reason below it never worked)
not properly cleaning simplesamlphp session in my code
So, first all of, I added a call to the cleanup method because it was missing on the real page, the code posted on my question is the test page, this is the real page where it was missing a call to cleanup.
$as->requireAuth(array(
'saml:idp' => $idp,
));
$attributes = $as->getAttributes();
$session = \SimpleSAML\Session::getSessionFromRequest();
if($session){
$session->cleanup();
}
Without calling cleanup() any value I put on the property session.phpsession.cookiename besides NULL ( =use PHP's setting) caused the session to completely break.
So after adding cleanup() I can now specify a value for the property session.phpsession.cookiename (\config\config.php).
I specified a value different (because this was the problem) from the name used by PHP, that is the default value PHPSESSID.
'session.phpsession.cookiename' => 'hSAMLses'
And now it's all working peacefully, hope this answer helps someone because I really struggled too much.

CSRF - Gracefully Fail Stale Sessions

We built a SaaS platform that allows users to create a message and share it across any advertising medium via an pre-determined alphanumeric code.
All of our forms use a CSRF token, but I noticed a problem. If a user is editing their message, then takes a break, and comes back to finish editing and hits save, they are faced with a 'CSRF mismatch' warning message. This is a critical problem, as they have to refresh the page, losing their changes, in order to regenerate a valid token.
What's happening is that the initial CSRF token is generated based on static params such as filename and php_uname(), but it is also being generated via session_id(). So if the session_id changes, a new CSRF token is generated and obviously does not match the old token resulting in the warning message.
It would be easy enough to disable CSRF protection on forms that require user authentication, but we have a few forms that do not require authentication, such as user login. Granted it's not a big deal to show the 'CSRF mismatch' on a user login, but it does look wonky to users who have no idea what that means, and end up going 'screw this' and leaving the site.
So I am looking for an alternative solution, to maintain some semblance of form protection, without generating a worthless warning message.
Two potential solutions...
1) Instead of generating a CSRF based off session_id, generate it based off some cookie value, this will allow graceful cross session handling.
2) Drop CSRF altogether. Instead, validate the REFERER of all form posts, and if they match the parent domain, then proceed. This will ensure other sites are not posting to our forms (the primary purpose of CSRF). We actually use this solution for our ajax requests.
We are leaning towards option #2 but wanted to check to see if there were any other graceful solutions to consider.
I ended up going with option #1 (Cookie solution), and treat the CSRF seeding just like a persistent login cookie. If the seed isn't available in current session, check cookie, if exists, populate current session. Seems to be working well and transcends stale sessions.

laravel and multi-sessions from the same browser

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'];

How to log out a banned user in Symfony?

Symfony provides a simple way of preventing users from logging in using the isEnabled property if the user class implements AdvancedUserInterface.
However, if the user is logged in nothing will prevent them from accessing the website until their session expires.
The idea would be to check the isEnabled property upon getting the user entity ($this->get('security.token_storage')->getToken()->getUser()) and to invalidate the session then. What is the preferred way of doing so?
Or is there a better way to achieve this goal?
Maybe catch request before controller execution (there is a listener for that I think), check if his account is blocked and if it's true just make 403 response and hasta la vista baby
You can set the following in your security.yml:
security:
always_authenticate_before_granting: true
This will always re-authenticate a user before storing the information into the token. It will take care of these kind of changes in the user configuration, but also things like changed roles etc..
Better way is redirecting them to the logout url, which is handled by Security system. You can do it by creating a listener which listens the kernel.request event. Here is a gist which I created about a month ago, which provides such functionality..
You could do this with a custom user provider (it's an easy change if you are already using one).
http://symfony.com/doc/current/cookbook/security/custom_provider.html
loadUserByUsername is called when a user logs in.
refreshUser is called on every subsequent request. The user-object is unserialized and then refreshed. You can then do whatever you want in the refresh method, including reloading it from the database and check if the user is banned.

How to reset a Zend rememberMe function on each automatic login?

I am using a remember me checkbox implemented with a Zend rememberMe function for automatic login. I have written this condition inside the controller for login, but I want it to reset to 7 days on each automatic login (as long as the current login is an automatic one).
$seconds = 60 * 60 * 24 * 7; // 7 days
Zend_Session::rememberMe($seconds);
Is there a default zend function to update the cookie during each access. Btw, I'm new to coding. Hope someone can help me out. Thanks.
You wouldn't want to set the cookie using Zend_Session::rememberMe() on each access. The reason for this is simply because each call to rememberMe() causes a new session id to be generated and the cookie is replaced by one with a new id. Data from the old session is copied to the new one and the old session is deleted.
Although no real harm would come out of this, there is overhead involved in doing this on each request. According to Zend, it is best practice to call this after the session has been started.
Also, if you did this unconditionally on each request, the only way you can differentiate between an automatic login after extended time and casual browsing would be to store the timestamp in the session and check it at the beginning of each request and set a time limit to determine a person as having gone away.
Instead, you could do this when a returning visitor goes to your login page to log in; you could then redirect them and they would be logged in without authenticating.
Or, if you want to update the session cookie periodically, you could call rememberMe() in your Bootstrap.php file after you have started the session. If you start the session from a plugin, or directly in the controllers, you should put the remember me call there after the session starts, and do it every so often (at your discretion).
See session_regenerate_id() which gets called when you call Zend_Session::rememberMe() and Zend Framework - Session Identifiers, as well as the following section on Session Hijacking and Fixation.
You cant use Zend_session to do that. Use cookies for make remember me.

Categories