Paste: Non-private session store

Author: dmpk2k
Mode: perl
Date: Sun, 21 Jun 2009 22:43:40
Plain Text |
package Session;

use Modern::Perl;
use signatures;

require Mojo::Cookie::Response;
require JSON;
require Digest::SHA;
require Clone;
require Test::Deep;
require Compress::Zlib;



my $SESSION_NAME      = 'session';
my $SESSION_HMAC_NAME = 'session_hmac';
my $SESSION_HMAC_KEY  = '091u23npu0298u1emo2i12s';



# session hashes
our %session;            # what other modules use
our %received_session;   # what we use to find out if anything changed during the request by deep-comparison with %session



# deserializes session data from the cookie, doing an integrity and authenticity check
sub load_session( $context ) {
  %session = ();
  %received_session = ();
  
  # if we didn't receive any cookies, leave the session empty
  my $cookie_ref = $context->req->cookies;
  return if not $cookie_ref;
  
  # extract the session and HMAC cookies
  my %cookies = map { $_->name => $_->value } @$cookie_ref;
  my $encoded_session       = $cookies{ $SESSION_NAME };
  my $expected_session_hmac = $cookies{ $SESSION_HMAC_NAME };
  
  return if not $encoded_session;
  
  # decode the session cookie and take an HMAC of it
  my $compressed_session  = MIME::Base64::decode( $encoded_session );
  my $serialized_session  = Compress::Zlib::memGunzip( $compressed_session );
  my $actual_session_hmac = Digest::SHA::hmac_sha256_base64( $serialized_session, $SESSION_HMAC_KEY );
  
  # check the session cookie wasn't manipulated by the client
  return if $actual_session_hmac ne $expected_session_hmac;
  
  # populate our session
  %session = %{ JSON::decode_json( $serialized_session ) };
  %received_session = %{ Clone::clone( \%session ) };
}



# serializes session data to cookie, adding an HMAC
sub save_session( $context ) {
  # we only send back a new session cookie if any session variables actually changed during this request
  return if Test::Deep::eq_deeply( \%session, \%received_session );
  
  # serialize, compress and BASE64 encode our session
  my $serialized_session = JSON::encode_json( \%session );
  my $compressed_session = Compress::Zlib::memGzip( $serialized_session );
  my $encoded_session    = MIME::Base64::encode( $compressed_session, '' );
  
  # we do an HMAC of the non-compressed/encoded version to leave less leeway for envelope manipulation
  my $session_hmac = Digest::SHA::hmac_sha256_base64( $serialized_session, $SESSION_HMAC_KEY );
  
  # create the session and HMAC cookies
  my $encoded_session_cookie = Mojo::Cookie::Response->new( name  => $SESSION_NAME,
                                                            value => $encoded_session,
                                                            path  => '/' );
  my $session_hmac_cookie    = Mojo::Cookie::Response->new( name  => $SESSION_HMAC_NAME,
                                                            value => $session_hmac,
                                                            path  => '/' );
  
  # warning: this replaces -all- existing cookies
  $context->res->cookies( ( $encoded_session_cookie, $session_hmac_cookie ) );
}



1;

New Annotation

Summary:
Author:
Mode:
Body: