[Catalyst-commits] r10831 - in Catalyst-Plugin-Session/0.00/trunk: lib/Catalyst/Plugin t t/lib

t0m at dev.catalyst.perl.org t0m at dev.catalyst.perl.org
Wed Jul 8 21:47:34 GMT 2009


Author: t0m
Date: 2009-07-08 21:47:34 +0000 (Wed, 08 Jul 2009)
New Revision: 10831

Modified:
   Catalyst-Plugin-Session/0.00/trunk/lib/Catalyst/Plugin/Session.pm
   Catalyst-Plugin-Session/0.00/trunk/t/lib/SessionTestApp.pm
   Catalyst-Plugin-Session/0.00/trunk/t/live_session_fixation.t
Log:
Merge branch paranoid_session_fixation_protection
svn merge -r10820:10830 http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Plugin-Session/0.00/branches/paranoid_session_fixation_protection/


Modified: Catalyst-Plugin-Session/0.00/trunk/lib/Catalyst/Plugin/Session.pm
===================================================================
--- Catalyst-Plugin-Session/0.00/trunk/lib/Catalyst/Plugin/Session.pm	2009-07-08 20:11:57 UTC (rev 10830)
+++ Catalyst-Plugin-Session/0.00/trunk/lib/Catalyst/Plugin/Session.pm	2009-07-08 21:47:34 UTC (rev 10831)
@@ -288,6 +288,24 @@
     $c->maybe::next::method(@_); # allow other plugins to hook in on this
 }
 
+sub change_session_id {
+    my $c = shift;
+
+    my $sessiondata = $c->session;
+    my $oldsid = $c->sessionid;
+    my $newsid = $c->create_session_id;
+		
+    if ($oldsid) {
+        $c->log->debug(qq/change_sessid: deleting session data from "$oldsid"/) if $c->debug;
+        $c->delete_session_data("${_}:${oldsid}") for qw/session expires flash/;
+    }
+
+    $c->log->debug(qq/change_sessid: storing session data to "$newsid"/) if $c->debug;
+    $c->store_session_data( "session:$newsid" => $sessiondata );
+
+    return $newsid; 
+}
+
 sub delete_session {
     my ( $c, $msg ) = @_;
 
@@ -749,6 +767,31 @@
 
 Note that these values are not auto extended.
 
+=item change_session_id
+
+By calling this method you can force a session id change while keeping all
+session data. This method might come handy when you are paranoid about some
+advanced variations of session fixation attack.
+
+If you want to prevent this session fixation scenario:
+
+    0) let us have WebApp with anonymous and authenticated parts
+    1) a hacker goes to vulnerable WebApp and gets a real sessionid, 
+       just by browsing anonymous part of WebApp
+    2) the hacker inserts (somehow) this values into a cookie in victim's browser
+    3) after the victim logs into WebApp the hacker can enter his/her session
+
+you should call change_session_id in your login controller like this:
+
+      if ($c->authenticate( { username => $user, password => $pass } )) {
+        # login OK
+        $c->change_session_id;
+        ...
+      } else {
+        # login FAILED
+        ...
+      }
+
 =back
 
 =head1 INTERNAL METHODS

Modified: Catalyst-Plugin-Session/0.00/trunk/t/lib/SessionTestApp.pm
===================================================================
--- Catalyst-Plugin-Session/0.00/trunk/t/lib/SessionTestApp.pm	2009-07-08 20:11:57 UTC (rev 10830)
+++ Catalyst-Plugin-Session/0.00/trunk/t/lib/SessionTestApp.pm	2009-07-08 21:47:34 UTC (rev 10831)
@@ -6,6 +6,8 @@
 use strict;
 use warnings;
 
+use Data::Dumper;
+
 __PACKAGE__->config->{session} = {
     # needed for live_verify_user_agent.t; should be harmless for other tests 
     verify_user_agent => 1,  
@@ -24,6 +26,37 @@
     $c->delete_session("logout");
 }
 
+sub set_session_variable : Global {
+    my ( $self, $c, $var, $val ) = @_;
+    $c->session->{$var} = $val;
+    $c->res->output("session variable set");
+}
+
+sub get_session_variable : Global {
+    my ( $self, $c, $var ) = @_;
+    my $val = $c->session->{$var} || 'n.a.';
+    $c->res->output("VAR_$var=$val");
+}
+
+sub get_sessid : Global {
+    my ( $self, $c ) = @_;
+    my $sid = $c->sessionid || 'n.a.';
+    $c->res->output("SID=$sid");
+}
+
+sub dump_session : Global {
+    my ( $self, $c ) = @_;
+    my $sid = $c->sessionid || 'n.a.';
+    my $dump = Dumper($c->session || 'n.a.');
+    $c->res->output("[SID=$sid]\n$dump");
+}
+
+sub change_sessid : Global {
+    my ( $self, $c ) = @_;
+    $c->change_session_id;
+    $c->res->output("session id changed");
+}
+
 sub page : Global {
     my ( $self, $c ) = @_;
     if ( $c->session_is_valid ) {

Modified: Catalyst-Plugin-Session/0.00/trunk/t/live_session_fixation.t
===================================================================
--- Catalyst-Plugin-Session/0.00/trunk/t/live_session_fixation.t	2009-07-08 20:11:57 UTC (rev 10830)
+++ Catalyst-Plugin-Session/0.00/trunk/t/live_session_fixation.t	2009-07-08 21:47:34 UTC (rev 10831)
@@ -4,6 +4,7 @@
 use warnings;
 
 use Test::More;
+use Data::Dumper;
 
 BEGIN {
     eval { require Catalyst::Plugin::Session::State::Cookie; Catalyst::Plugin::Session::State::Cookie->VERSION(0.03) }
@@ -17,20 +18,74 @@
     or plan skip_all =>
         'Test::WWW::Mechanize::Catalyst >= 0.51 is required for this test';
 
-    plan tests => 2;
+    plan tests => 10;
 }
 
 use lib "t/lib";
 use Test::WWW::Mechanize::Catalyst "SessionTestApp";
 
+#try completely random cookie unknown for our application; should be rejected
 my $injected_cookie = "sessiontestapp_session=89c3a019866af6f5a305e10189fbb23df3f4772c";
 
 my $ua1 = Test::WWW::Mechanize::Catalyst->new;
 $ua1->add_header('Cookie' => $injected_cookie);
 
 my $res = $ua1->get( "http://localhost/login" );
-my $cookie = $res->header('Set-Cookie');
+my $cookie1 = $res->header('Set-Cookie');
 
-ok $cookie;
-isnt $cookie, qr/$injected_cookie/, 'Logging in generates us a new cookie';
+ok $cookie1, "Set-Cookie 1";
+isnt $cookie1, qr/$injected_cookie/, "Logging in generates us a new cookie";
 
+$ua1->get( "http://localhost/get_sessid" );
+my $sid1 = $ua1->content;
+
+#set session variable var1 before session id change
+$ua1->get( "http://localhost/set_session_variable/var1/set_before_change");
+$ua1->get( "http://localhost/get_session_variable/var1");
+$ua1->content_is("VAR_var1=set_before_change");
+
+#just diagnostic dump
+$ua1->get( "http://localhost/dump_session" );
+#diag "Before-change:".$ua1->content;
+
+#change session id; all session data should be kept; old session id invalidated
+my $res2 = $ua1->get( "http://localhost/change_sessid" );
+my $cookie2 = $res2->header('Set-Cookie');
+
+ok $cookie2, "Set-Cookie 2";
+isnt $cookie2, $cookie1, "Cookie changed";
+
+$ua1->get( "http://localhost/get_sessid" );
+my $sid2 = $ua1->content;
+isnt $sid2, $sid1, 'SID changed';
+
+#just diagnostic dump
+$ua1->get( "http://localhost/dump_session" );
+#diag "After-change:".$ua1->content;
+
+#set session variable var2 after session id change
+$ua1->get( "http://localhost/set_session_variable/var2/set_after_change");
+
+#check if var1 and var2 contain expected values
+$ua1->get( "http://localhost/get_session_variable/var1");
+$ua1->content_is("VAR_var1=set_before_change");
+$ua1->get( "http://localhost/get_session_variable/var2");
+$ua1->content_is("VAR_var2=set_after_change");
+
+#just diagnostic dump
+$ua1->get( "http://localhost/dump_session" );
+#diag "End1:".$ua1->content;
+
+#try to use old cookie value (before session_id_change)
+my $ua2 = Test::WWW::Mechanize::Catalyst->new;
+$ua2->add_header('Cookie' => $cookie1);
+
+#if we take old cookie we should not be able to get any old session data
+$ua2->get( "http://localhost/get_session_variable/var1");
+$ua2->content_is("VAR_var1=n.a.");
+$ua2->get( "http://localhost/get_session_variable/var2");
+$ua2->content_is("VAR_var2=n.a.");
+
+#just diagnostic dump
+$ua2->get( "http://localhost/dump_session" );
+#diag "End2:".$ua2->content;




More information about the Catalyst-commits mailing list