[Bps-public-commit] rt-extension-resetpassword branch, sha256-instead-of-md5-for-token-generation, updated. 1.04-21-gdd1b0b2

Dianne Skoll dianne at bestpractical.com
Fri Sep 25 14:42:04 EDT 2020


The branch, sha256-instead-of-md5-for-token-generation has been updated
       via  dd1b0b238de92e05cdf6eb67a3c73500ab8e1968 (commit)
      from  66122afab012c331a2c7f1b96fb004ac3487cc16 (commit)

Summary of changes:
 html/NoAuth/ResetPassword/Reset/dhandler | 134 +++++++++++++++++++++----------
 1 file changed, 90 insertions(+), 44 deletions(-)

- Log -----------------------------------------------------------------
commit dd1b0b238de92e05cdf6eb67a3c73500ab8e1968
Author: Dianne Skoll <dianne at bestpractical.com>
Date:   Fri Sep 25 14:34:49 2020 -0400

    Make the reset password handler set a cookie and redirect to a URL that does not expose sensitive information.
    
    The reset-password link contains a potentially-sensitive token.  Although
    the possibility for abuse is small since the token is invalidated after
    it has been used, we don't want to leak the token in any Referrer: headers.
    
    By storing the sensitive information in a cookie and immediately redirecting,
    we should never see the token in a Referrer header.

diff --git a/html/NoAuth/ResetPassword/Reset/dhandler b/html/NoAuth/ResetPassword/Reset/dhandler
index 5436468..f58bfaf 100644
--- a/html/NoAuth/ResetPassword/Reset/dhandler
+++ b/html/NoAuth/ResetPassword/Reset/dhandler
@@ -8,18 +8,60 @@ my $show_form    = 1;
 my $title        = loc('Reset your password');
 my $virtual_path = $m->dhandler_arg();
 my ( $submitted_token, $id ) = split( '/', $virtual_path );
-my $token;
-# Validate the token
-my $u = RT::User->new($RT::SystemUser);
-$u->LoadByCols( id => $id );
-if ( $u->id ) {
-    $token = RT::Extension::ResetPassword::CreateToken($u) || '';
+
+my $bland_error_message = loc(
+        "It looks like the URL you clicked on has expired or wasn't quite right. Maybe you didn't paste the whole thing?"
+);
+
+my $cookie_name = 'RTPasswdResetCookie';
+
+# If the token and ID are part of the URL path, set a cookie and redirect.
+# We do not want the original URL to accidentally leak info in the
+# Referrer field
+if ($submitted_token && $id) {
+    my $cookie = CGI::Cookie->new(
+        -name     => $cookie_name,
+        -value    => "$submitted_token/$id",
+        -path     => RT->Config->Get('WebPath'),
+        -secure   => (RT->Config->Get('WebSecureCookies') ? 1 : 0),
+        -httponly => (RT->Config->Get('WebHttpOnlyCookies') ? 1 : 0)
+        );
+    $r->err_headers_out->{'Set-Cookie'} = $cookie->as_string;
+    $r->status(302);
+    $m->redirect(RT->Config->Get('WebPath') . '/NoAuth/ResetPassword/Reset/',
+                 '302 found');
+    $m->abort;
 }
-else {
-    push @results,
-        loc("Something went wrong. Please contact your RT administrator.");
+
+# If we don't have a cookie, something's wrong
+my %cookies = CGI::Cookie->parse(RT::Interface::Web::RequestENV('HTTP_COOKIE'));
+
+if (!$cookies{$cookie_name}) {
+   push @results, $bland_error_message;
     $show_form = 0;
 }
+if ($show_form) {
+    ( $submitted_token, $id ) = split( '/', $cookies{$cookie_name}->value());
+    if (!$submitted_token || !$id) {
+        push @results, $bland_error_message;
+        $show_form = 0;
+    }
+}
+
+my $token;
+my $u = RT::User->new($RT::SystemUser);
+if ($show_form) {
+    # Validate the token
+    $u->LoadByCols( id => $id );
+    if ( $u->id ) {
+        $token = RT::Extension::ResetPassword::CreateToken($u) || '';
+    }
+    else {
+        push @results,
+            loc("Something went wrong. Please contact your RT administrator.");
+        $show_form = 0;
+    }
+}
 
 # Calculate time difference between now and when user object was updated
 my $age = $u->LastUpdatedObj->Diff;
@@ -37,44 +79,48 @@ if (!defined($age)) {
     }
 }
 
-# If the token validation fails, throw them an error
-if ( $submitted_token ne $token ) {
-    push @results,
-        loc(
-        "It looks like the URL you clicked on has expired or wasn't quite right. Maybe you didn't paste the whole thing?"
-        );
-    $show_form = 0;
-}
-
-# If the link has expired, throw the same error.  Default expiry time is 4 hours
-elsif ( ($age < 0) ||
-        ($age > (RT->Config->Get('PasswordChangeLinkExpirySeconds') || (4*60*60)))) {
-    push @results,
-        loc(
-        "It looks like the URL you clicked on has expired or wasn't quite right. Maybe you didn't paste the whole thing?"
-        );
-    $show_form = 0;
-}
-
-# Link is valid and has not expired
-else {
-
-    # If they've supplied a new password twice, change it and redirect to home
-    if (    ( $submitted_token eq $token )
-        and $ARGS{'password'}
-        and $ARGS{'password2'}
-        and ( $ARGS{'password'} eq $ARGS{'password2'} ) )
-    {
-        my ( $val, $msg ) = $u->SetPassword( $ARGS{'password'} );
-        push @results, $msg;
-        if ($val) { $show_form = 0;}
+if ($show_form) {
+    # If the token validation fails, throw them an error
+    if ( $submitted_token ne $token ) {
+        push @results, $bland_error_message;
+        $show_form = 0;
     }
-    elsif ( $ARGS{'password'} ) {
-        push @results, loc("The two passwords you typed didn't match.");
+    # If the link has expired, throw the same error.  Default expiry time is 4 hours
+    elsif ( ($age < 0) ||
+            ($age > (RT->Config->Get('PasswordChangeLinkExpirySeconds') || (4*60*60)))) {
+        push @results, $bland_error_message;
+        $show_form = 0;
     }
+    # Link is valid and has not expired
+    else {
+        # If they've supplied a new password twice, change it and redirect to home
+        if (    ( $submitted_token eq $token )
+                and $ARGS{'password'}
+                and $ARGS{'password2'}
+                and ( $ARGS{'password'} eq $ARGS{'password2'} ) )
+        {
+            my ( $val, $msg ) = $u->SetPassword( $ARGS{'password'} );
+            push @results, $msg;
+            if ($val) { $show_form = 0;}
+            # Ask the browser to delete the cookie
+            my $cookie = CGI::Cookie->new(
+                -name     => $cookie_name,
+                -value    => '',
+                -expires  => '-10y',
+                -path     => RT->Config->Get('WebPath'),
+                -secure   => (RT->Config->Get('WebSecureCookies') ? 1 : 0),
+                -httponly => (RT->Config->Get('WebHttpOnlyCookies') ? 1 : 0)
+                );
+            $r->err_headers_out->{'Set-Cookie'} = $cookie->as_string;
 
+        }
+        elsif ( $ARGS{'password'} ) {
+            push @results, loc("The two passwords you typed didn't match.");
+        }
+    }
 }
-# otherwise, show the form
+
+# otherwise, (potentially) show the form
 
 
 </%init>
@@ -101,4 +147,4 @@ else {
 <input type ="submit" value ="<%loc('Change password')%>">
 </form>
 %}
-<a href="<%$RT::WebURL|n%>"><&|/l&>Login</&></a>
+<a href="<% RT->Config->Get('WebPath') %>"><&|/l&>Login</&></a>

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list