Invalidating Laravel log-in sessions on password change

folder_openLaravel, PHP, Security, Technology
comment2 Comments

For security reasons it’s fairly good practice to invalidate all log-in sessions when a users password is changed. This is especially useful when a users account has been compromised and they go to change or reset their password. Without log-in session invalidation the attacker will still be logged in and able to cause chaos.

Unfortunately Laravel does not provide this functionality out of the box. We actually have to go through quite a bit of trouble to make Laravel play ball here but it’s definitely worth it, so lets get to it!

Implementing this feature is a two step process.

  1. Track the session IDs of all logged in users.
  2. Invalidate the session data attached to those user sessions.

Part one is fairly simple and can be done in your own application code. The session ID is exposed through Session::getId() so simply add this to an array of session ids stored in a persistent cache. When When you want to invalidate the log-in sessions simply fetch this cached array. I will leave this part as an exercise to the reader.

Part two is where it gets tricky. The Laravel session providers themselves implement a very suitable destroy method that takes a session ID. However, unfortunately the Laravel session store does not expose this method but instead implements a migrate() method. This method does not take a session ID, but instead offers to destroy the current session. Invalidating the session of the user changing the password is all perfectly fine, but that still leaves our attacker logged in. In order to fix this we need to implement a custom session store that properly exposes the destroy() method of the individual session providers.

The following code is done in Laravel 4.2. Version 5 has quite a few changes so this might be significantly different. If anyone uses Laravel 5 please let me know if this applies there as well.

In order to implement this we need to backtrack to where Laravel actually loads session classes. This is defined in your app.php providers array as ‘Illuminate\Session\SessionServiceProvider’. The SessionServiceProvider registers a SessionManager which then creates the Store class we’re looking to extend. This means we need to provide our own version of these 3 classes and then modify app.php to load our own SessionServiceProvider class.

<?php 
namespace mfjordvald\Session;

class SessionServiceProvider extends \Illuminate\Session\SessionServiceProvider
{
	/**
	 * Register the session manager instance.
	 *
	 * @return void
	 */
	protected function registerSessionManager()
	{
		$this->app->bindShared('session', function($app)
		{
			return new SessionManager($app);
		});
	}
}
<?php 
namespace mfjordvald\Session;

class SessionManager extends \Illuminate\Session\SessionManager
{
	/**
	 * Build the session instance.
	 *
	 * @param  \SessionHandlerInterface  $handler
	 * @return \Illuminate\Session\Store
	 */
	protected function buildSession($handler)
	{
		return new Store($this->app['config']['session.cookie'], $handler);
	}
}

<?php 
namespace mfjordvald\Session;

class Store extends \Illuminate\Session\Store
{
	/**
	 * Destroy a session by ID.
	 */
	public function destroy($session_id)
	{
		$this->handler->destroy($session_id);
	}
}

Finally, with all these classes implemented we can update our provider array to:

'mfjordvald\Session\SessionServiceProvider'

and now we have access to a Session::destroy($session_id) method we can use to invalidate all sessions attached to a specific user account.

Related Posts

2 Comments. Leave new

  • How to get all user’s sessions across all the devices?

    Reply
  • You can do nothing when password’s been reseted because you doesn’t have session_id to destroy_it.
    How can we get all sessions?
    Or this method doesn’t work

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.