mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-03-31 04:54 am

[dw-free] Verify that bad-password checking is implemented & enabled

[commit: http://hg.dwscoalition.org/dw-free/rev/bed7ce2ec73a]

http://bugs.dwscoalition.org/show_bug.cgi?id=446

Add some basic password checking.

Patch by [personal profile] janinedog.

Files modified:
  • bin/upgrading/en.dat
  • cgi-bin/LJ/CreatePage.pm
  • cgi-bin/LJ/Widget/CreateAccount.pm
  • cgi-bin/ljprotocol.pl
  • htdocs/changepassword.bml
  • htdocs/inc/common-passwords
  • htdocs/stc/widgets/createaccount.css
--------------------------------------------------------------------------------
diff -r ef89676071eb -r bed7ce2ec73a bin/upgrading/en.dat
--- a/bin/upgrading/en.dat	Tue Mar 31 04:43:49 2009 +0000
+++ b/bin/upgrading/en.dat	Tue Mar 31 04:54:38 2009 +0000
@@ -2215,8 +2215,6 @@ optional=(optional)
 
 password=Password
 
-password.max30=Passwords may not be longer than 30 characters.
-
 pingback.ljping.comment.text=User <lj user="[[poster]]"> referenced to your post from <a href="[[sourceURI]]">[[subject]]</a> saying: [...] [[context]] [...]
 
 pingback.option.disabled=Disabled
@@ -3469,7 +3467,23 @@ widget.createaccount.error.password.bad=
 
 widget.createaccount.error.password.blank=You must enter a password.
 
+widget.createaccount.error.password.common=Password must not be based on a common word.
+
+widget.createaccount.error.password.likeemail=Password must not be similar to your email address.
+
+widget.createaccount.error.password.likename=Password must not be similar to your displayed name.
+
+widget.createaccount.error.password.likeusername=Password must not be similar to your username.
+
+widget.createaccount.error.password.needsmoreuniquechars=Password must contain at least 4 unique characters.
+
+widget.createaccount.error.password.needsnonletter=Password must contain at least one non-letter character (digit or symbol).
+
 widget.createaccount.error.password.nomatch=Passwords do not match.
+
+widget.createaccount.error.password.toolong=Password must not be longer than 30 characters.
+
+widget.createaccount.error.password.tooshort=Password must be at least 6 characters.
 
 widget.createaccount.error.username.inuse=Sorry, this username is already in use.
 
diff -r ef89676071eb -r bed7ce2ec73a cgi-bin/LJ/CreatePage.pm
--- a/cgi-bin/LJ/CreatePage.pm	Tue Mar 31 04:43:49 2009 +0000
+++ b/cgi-bin/LJ/CreatePage.pm	Tue Mar 31 04:54:38 2009 +0000
@@ -66,4 +66,82 @@ sub verify_username {
     return $error;
 }
 
+sub verify_password {
+    my $class = shift;
+    my %opts = @_;
+
+    return undef unless LJ::is_enabled( 'password_check' );
+
+    my ( $password, $username, $email, $name );
+    my $u = $opts{u};
+    if ( LJ::isu( $u ) ) {
+        $password = $u->password;
+        $username = $u->user;
+        $email = $u->email_raw;
+        $name = $u->name_raw;
+    }
+
+    $password = $opts{password};
+    $username = $opts{username};
+    $email = $opts{email};
+    $name = $opts{name};
+
+    # password must exist
+    return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.blank' )
+        unless $password;
+
+    # at least 6 characters
+    return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.tooshort' )
+        if length $password < 6;
+
+    # no more than 30 characters
+    return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.toolong' )
+        if length $password > 30;
+
+    # only ascii characters
+    return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.asciionly' )
+        unless LJ::is_ascii( $password );
+
+    # not the same as the username or the reversed username
+    if ( $username ) {
+        return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.likeusername' )
+            if lc $password eq lc $username || lc $password eq lc reverse $username;
+    }
+
+    # not the same as either part of the email address
+    if ( $email ) {
+        $email =~ /^(.+)@(.+)\./;
+        return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.likeemail' )
+            if lc $password eq lc $1 || lc $password eq lc $2;
+    }
+
+    # not the same as the displayed name or the reversed displayed name
+    if ( $name ) {
+        return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.likename' )
+            if lc $password eq lc $name || lc $password eq lc reverse $name;
+    }
+
+    # at least 4 unique characters
+    my %unique_chars = map { $_ => 1 } split( //, $password );
+    return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.needsmoreuniquechars' )
+        unless scalar keys %unique_chars > 4;
+
+    # contains at least one digit or symbol
+    return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.needsnonletter' )
+        if $password =~ /^[A-Za-z]+$/;
+
+    # isn't similar to a common password
+    my @common_passwords = grep { $_ } split( /\r?\n/, LJ::load_include( 'common-passwords' ) );
+    foreach my $comm_pass ( $LJ::SITENAMESHORT, @common_passwords ) {
+        # you can have a common password in your password if your password is greater in length
+        # than the sum of the common password's length plus the ceiling of half of its length
+        next if length $password > ( ( length $comm_pass ) + POSIX::ceil( ( length $comm_pass ) / 2 ) );
+
+        return LJ::Widget::CreateAccount->ml( 'widget.createaccount.error.password.common' )
+            if $password =~ /$comm_pass/i;
+    }
+
+    return undef;
+}
+
 1;
diff -r ef89676071eb -r bed7ce2ec73a cgi-bin/LJ/Widget/CreateAccount.pm
--- a/cgi-bin/LJ/Widget/CreateAccount.pm	Tue Mar 31 04:43:49 2009 +0000
+++ b/cgi-bin/LJ/Widget/CreateAccount.pm	Tue Mar 31 04:54:38 2009 +0000
@@ -387,27 +387,14 @@ sub handle_post {
     $post->{password1} = LJ::trim($post->{password1});
     $post->{password2} = LJ::trim($post->{password2});
 
-    if ($post->{password1} ne $post->{password2}) {
-        $from_post{errors}->{confirmpass} = $class->ml('widget.createaccount.error.password.nomatch');
+    if ( !$post->{password1} ) {
+        $from_post{errors}->{password} = $class->ml( 'widget.createaccount.error.password.blank' );
+    } elsif ( $post->{password1} ne $post->{password2} ) {
+        $from_post{errors}->{confirmpass} = $class->ml( 'widget.createaccount.error.password.nomatch' );
     } else {
-        my $checkpass = LJ::run_hook("bad_password", {
-            user => $user,
-            email => $email,
-            password => $post->{password1},
-        });
-
-        if ($checkpass) {
-            $from_post{errors}->{password} = $class->ml('widget.createaccount.error.password.bad') . " $checkpass";
-        }
-    }
-    if (!$post->{password1}) {
-        $from_post{errors}->{password} = $class->ml('widget.createaccount.error.password.blank');
-    } elsif (length $post->{password1} > 30) {
-        $from_post{errors}->{password} = LJ::Lang::ml('password.max30');
-    }
-
-    unless (LJ::is_ascii($post->{password1})) {
-        $from_post{errors}->{password} = $class->ml('widget.createaccount.error.password.asciionly');
+        my $checkpass = LJ::CreatePage->verify_password( password => $post->{password1}, username => $user, email => $email );
+        $from_post{errors}->{password} = $class->ml( 'widget.createaccount.error.password.bad' ) . " $checkpass"
+            if $checkpass;
     }
 
     # age checking to determine how old they are
@@ -467,7 +454,7 @@ sub handle_post {
                     $post->{'recaptcha_challenge_field'}, $post->{'recaptcha_response_field'}
                 );
 
-               $from_post{errors}->{captcha} = $class->ml('widget.createaccount.error.captcha.invalid') unless $result->{'is_valid'} eq '1';
+                $from_post{errors}->{captcha} = $class->ml('widget.createaccount.error.captcha.invalid') unless $result->{'is_valid'} eq '1';
             } else {
                 $from_post{errors}->{captcha} = $class->ml('widget.createaccount.error.captcha.invalid');
             }
diff -r ef89676071eb -r bed7ce2ec73a cgi-bin/ljprotocol.pl
--- a/cgi-bin/ljprotocol.pl	Tue Mar 31 04:43:49 2009 +0000
+++ b/cgi-bin/ljprotocol.pl	Tue Mar 31 04:54:38 2009 +0000
@@ -2514,7 +2514,7 @@ sub login_message
     return $msg->("not_validated")     if ($u->{'status'} eq "N" and not $LJ::EVERYONE_VALID);
     return $msg->("must_revalidate")   if ($u->{'status'} eq "T" and not $LJ::EVERYONE_VALID);
 
-    my $checkpass = LJ::run_hook("bad_password", { 'u' => $u });
+    my $checkpass = LJ::CreatePage->verify_password( u => $u );
     return $msg->("bad_password", { 'pre' => "$checkpass " }) if $checkpass;
 
     return $msg->("old_win32_client")  if $req->{'clientversion'} =~ /^Win32-MFC\/(1.2.[0123456])$/;
diff -r ef89676071eb -r bed7ce2ec73a htdocs/changepassword.bml
--- a/htdocs/changepassword.bml	Tue Mar 31 04:43:49 2009 +0000
+++ b/htdocs/changepassword.bml	Tue Mar 31 04:54:38 2009 +0000
@@ -134,33 +134,21 @@ body<=
              }
          }
      }
-     if ($newpass1 ne $newpass2) {
+
+     if ( !$newpass1 ) {
+         push @errors, $ML{'.error.blankpassword'};
+     } elsif ( $newpass1 ne $newpass2 ) {
          push @errors, $ML{'.error.badnewpassword'};
      } else {
-         if ($newpass1 eq "") {
-             push @errors, $ML{'.error.blankpassword'};
-         } elsif (length $newpass1 > 30) {
-             push @errors, $ML{'.error.characterlimit'};
-         } else {
-             my $checkpass = LJ::run_hook("bad_password",
-                                           {
-                                               'u'        => $u,
-                                               'password' => $newpass1,
-                                           });
-             if ($checkpass) {
-                 push @errors, BML::ml('.error.badcheck', {'error' => $checkpass});
-             }
-         }
+         my $checkpass = LJ::CreatePage->verify_password( password => $newpass1, u => $u );
+         push @errors, BML::ml( '.error.badcheck', { error => $checkpass } )
+             if $checkpass;
      }
 
      # don't allow changes if email address is not validated, unless they
      # have a bad password or got the reset email
      if ($u->{'status'} ne 'A' && !$u->prop('badpassword') && !$authu) {
          push @errors, $ML{'.error.notvalidated'};
-     }
-
-     unless (LJ::is_ascii($newpass1)) {
-         push @errors, $ML{'.error.nonascii'};
      }
 
      if (@errors) {
diff -r ef89676071eb -r bed7ce2ec73a htdocs/inc/common-passwords
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/inc/common-passwords	Tue Mar 31 04:54:38 2009 +0000
@@ -0,0 +1,13 @@
+password
+test
+blink182
+qwerty
+letmein
+abc123
+monkey
+myspace1
+password1
+123456
+12345678
+1234
+12345
diff -r ef89676071eb -r bed7ce2ec73a htdocs/stc/widgets/createaccount.css
--- a/htdocs/stc/widgets/createaccount.css	Tue Mar 31 04:43:49 2009 +0000
+++ b/htdocs/stc/widgets/createaccount.css	Tue Mar 31 04:54:38 2009 +0000
@@ -57,4 +57,6 @@ span.appwidget-createaccount #username_e
 .create-form span.formitemFlag {
     display: block;
     width: 330px;
+    font-weight: bold;
+    color: #f00;
 }
--------------------------------------------------------------------------------

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
No Subject Icon Selected
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org