change_session_id

It is considered good security practice, and is a requirement of a number of established security standards, to change session ID on any change of privilege level. For starters you should consider doing this on user login, and possibly at any other time a user has some privilege added or removed.

Session destruction

Destroying a session in Dancer2 has always been possible using Dancer2::Core::App/destroy_session like so:

app->destroy_session;

Session destruction by its very nature causes the session ID to change since the server instructs the client to forget the session ID, send it a new session ID and also destroys any session data held on the server. This is considered a "good thing" when a user logs out of an application but it is not very useful when a user logs in and you want to keep existing session data such as cart products.

change_session_id

Dancer2::Core::App/change_session_id to the rescue!

If you are using version 0.610 or later of Dancer2::Plugin::Auth::Extensible to handle authentication and authorisation then you already have change_session_id called on successful login. If you are using some other solution then you want to do something like this:

post '/login' => sub {
    my $username = body_parameters->get("username");
    my $password = body_parameters->get("password");

    if ( my_authentication_check( $username, $password ) ) {
        # login successful
        app->change_session_id;
    }
};

You might also want to consider calling change_session_id after successful password change.

The gory details

This part is particularly relevant to implementors of session engines.

Native session engine support

All session engines should implement the private method _change_id which is called like so:

$session_engine->_change_id( $old_id, $new_id )

Where $old_id and $new_id are the old and new session IDs respectively.

The session engine should do whatever is necessary for the ID change to be successful such as changing the key used in the session storage.

All file-based session engines including Dancer2::Session::YAML and Dancer2::Session::JSON get native support for change_session_id since they consume the Dancer2::Core::Role::SessionFactory::File which provides _change_id.

Non-native fallback support

If you have a session engine that doesn't implement _change_id then don't worry: we added a fallback mechanism to Dancer2 which does this:

my $session = $self->session;

# grab data, destroy session and store data again
my %data = %{$session->data};

# destroy existing session
$self->destroy_session;

# get new session
$session = $self->session;

# write data from old session into new
# Some engines add session id to data so skip id.
while (my ($key, $value) = each %data ) {
    $session->write($key => $value) unless $key eq 'id';
}

# clear out destroyed session - no longer relevant
$self->clear_destroyed_session;

This fallback mechanism will hopefully be removed some time in the future.

Further reading

OWASP Session Management Cheat Sheet.

Author

This article has been written by Peter Mottram for the Perl Dancer Advent Calendar 2016.

Copyright

No copyright retained. Enjoy.

2016 // Peter Mottram (SysPete) <peter@sysnix.com>