X-Git-Url: http://git.uio.no/git/?p=usit-rt.git;a=blobdiff_plain;f=lib%2FRT%2FInterface%2FWeb.pm;h=3cc35b050d679b19f58f7d7522b8b60bd38bdc90;hp=f04d0cc14d1a8353bae31baa6829b194d07aa41a;hb=35ef43cf382dbbdaeeeb9a33484873f893e00a51;hpb=84fb5b46b6d278400211552181bac9e6def7d09d diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm index f04d0cc..3cc35b0 100644 --- a/lib/RT/Interface/Web.pm +++ b/lib/RT/Interface/Web.pm @@ -253,6 +253,7 @@ sub HandleRequest { ValidateWebConfig(); DecodeARGS($ARGS); + local $HTML::Mason::Commands::DECODED_ARGS = $ARGS; PreprocessTimeUpdates($ARGS); InitializeMenu(); @@ -539,6 +540,10 @@ sub ShowRequestedPage { my $m = $HTML::Mason::Commands::m; + # Ensure that the cookie that we send is up-to-date, in case the + # session-id has been modified in any way + SendSessionCookie(); + # precache all system level rights for the current user $HTML::Mason::Commands::session{CurrentUser}->PrincipalObj->HasRights( Object => RT->System ); @@ -690,7 +695,6 @@ sub AttemptPasswordAuthentication { InstantiateNewSession(); $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj; - SendSessionCookie(); $m->callback( %$ARGS, CallbackName => 'SuccessfulLogin', CallbackPage => '/autohandler' ); @@ -745,6 +749,7 @@ sub LoadSessionFromCookie { sub InstantiateNewSession { tied(%HTML::Mason::Commands::session)->delete if tied(%HTML::Mason::Commands::session); tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', undef; + SendSessionCookie(); } sub SendSessionCookie { @@ -1161,7 +1166,7 @@ sub ComponentRoots { return @roots; } -my %is_whitelisted_path = ( +our %is_whitelisted_component = ( # The RSS feed embeds an auth token in the path, but query # information for the search. Because it's a straight-up read, in # addition to embedding its own auth, it's fine. @@ -1172,7 +1177,7 @@ sub IsCompCSRFWhitelisted { my $comp = shift; my $ARGS = shift; - return 1 if $is_whitelisted_path{$comp}; + return 1 if $is_whitelisted_component{$comp}; my %args = %{ $ARGS }; @@ -1200,6 +1205,11 @@ sub IsCompCSRFWhitelisted { delete $args{results} if $args{results} and $HTML::Mason::Commands::session{"Actions"}->{$args{results}}; + # The homepage refresh, which uses the Refresh header, doesn't send + # a referer in most browsers; whitelist the one parameter it reloads + # with, HomeRefreshInterval, which is safe + delete $args{HomeRefreshInterval}; + # If there are no arguments, then it's likely to be an idempotent # request, which are not susceptible to CSRF return 1 if !%args; @@ -1209,11 +1219,16 @@ sub IsCompCSRFWhitelisted { sub IsRefererCSRFWhitelisted { my $referer = _NormalizeHost(shift); - my $config = _NormalizeHost(RT->Config->Get('WebBaseURL')); + my $base_url = _NormalizeHost(RT->Config->Get('WebBaseURL')); + $base_url = $base_url->host_port; - return (1,$referer,$config) if $referer->host_port eq $config->host_port; + my $configs; + for my $config ( $base_url, RT->Config->Get('ReferrerWhitelist') ) { + push @$configs,$config; + return 1 if $referer->host_port eq $config; + } - return (0,$referer,$config); + return (0,$referer,$configs); } =head3 _NormalizeHost @@ -1268,12 +1283,21 @@ EOT "your browser did not supply a Referrer header", # loc ) if !$ENV{HTTP_REFERER}; - my ($whitelisted, $browser, $config) = IsRefererCSRFWhitelisted($ENV{HTTP_REFERER}); + my ($whitelisted, $browser, $configs) = IsRefererCSRFWhitelisted($ENV{HTTP_REFERER}); return 0 if $whitelisted; + if ( @$configs > 1 ) { + return (1, + "the Referrer header supplied by your browser ([_1]) is not allowed by RT's configured hostname ([_2]) or whitelisted hosts ([_3])", # loc + $browser->host_port, + shift @$configs, + join(', ', @$configs) ); + } + return (1, "the Referrer header supplied by your browser ([_1]) is not allowed by RT's configured hostname ([_2])", # loc - $browser->host_port, $config->host_port); + $browser->host_port, + $configs->[0]); } sub ExpandCSRFToken { @@ -1290,6 +1314,7 @@ sub ExpandCSRFToken { return unless $user->ValidateAuthString( $data->{auth}, $token ); %{$ARGS} = %{$data->{args}}; + $HTML::Mason::Commands::DECODED_ARGS = $ARGS; # We explicitly stored file attachments with the request, but not in # the session yet, as that would itself be an attack. Put them into @@ -1304,21 +1329,9 @@ sub ExpandCSRFToken { return 1; } -sub MaybeShowInterstitialCSRFPage { +sub StoreRequestToken { my $ARGS = shift; - return unless RT->Config->Get('RestrictReferrer'); - - # Deal with the form token provided by the interstitial, which lets - # browsers which never set referer headers still use RT, if - # painfully. This blows values into ARGS - return if ExpandCSRFToken($ARGS); - - my ($is_csrf, $msg, @loc) = IsPossibleCSRF($ARGS); - return if !$is_csrf; - - $RT::Logger->notice("Possible CSRF: ".RT::CurrentUser->new->loc($msg, @loc)); - my $token = Digest::MD5::md5_hex(time . {} . $$ . rand(1024)); my $user = $HTML::Mason::Commands::session{'CurrentUser'}->UserObj; my $data = { @@ -1337,10 +1350,28 @@ sub MaybeShowInterstitialCSRFPage { $HTML::Mason::Commands::session{'CSRF'}->{$token} = $data; $HTML::Mason::Commands::session{'i'}++; + return $token; +} + +sub MaybeShowInterstitialCSRFPage { + my $ARGS = shift; + + return unless RT->Config->Get('RestrictReferrer'); + + # Deal with the form token provided by the interstitial, which lets + # browsers which never set referer headers still use RT, if + # painfully. This blows values into ARGS + return if ExpandCSRFToken($ARGS); + + my ($is_csrf, $msg, @loc) = IsPossibleCSRF($ARGS); + return if !$is_csrf; + + $RT::Logger->notice("Possible CSRF: ".RT::CurrentUser->new->loc($msg, @loc)); + my $token = StoreRequestToken($ARGS); $HTML::Mason::Commands::m->comp( '/Elements/CSRF', - OriginalURL => $HTML::Mason::Commands::r->path_info, + OriginalURL => RT->Config->Get('WebPath') . $HTML::Mason::Commands::r->path_info, Reason => HTML::Mason::Commands::loc( $msg, @loc ), Token => $token, );