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;
 }
--------------------------------------------------------------------------------
ext_78: A picture of a plush animal. It looks a bit like a cross between a duck and a platypus. (Default)

[identity profile] pne.livejournal.com 2009-03-31 08:56 am (UTC)(link)
The 30-character max-length check doesn't match http://www.dreamwidth.org/support/faqbrowse.bml?faqid=39, which suggests using a password like "This sentence will let me in to my Dreamwidth account, easy as 1-2-3!" and promises that "Dreamwidth allows very lengthy passwords that can include spaces and punctuation".