diff options
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Auth.pm')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth.pm | 57 |
1 files changed, 43 insertions, 14 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index cecfa318c..0d0c2240a 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -448,6 +448,9 @@ sub check_csrf_token : Private { unless $time && $time > time() - 3600 && $token eq $gen_token; + + # Also check recaptcha if needed + $c->cobrand->call_hook('check_recaptcha'); } sub no_csrf_token : Private { @@ -457,7 +460,7 @@ sub no_csrf_token : Private { =item common_password -Returns 1/0 depending on if password is common or not. +Returns 1/0 depending on if password is common/breached or not. =cut @@ -466,10 +469,8 @@ sub common_password : Local : Args(0) { my $password = $c->get_param('password_register'); - my $return = JSON->true; - if (!$c->cobrand->call_hook('bypass_password_checks') && found($password)) { - $return = _('Please choose a less commonly-used password'); - } + my $pass = $c->forward('test_password', [ $password ]); + my $return = $pass ? JSON->true : $c->stash->{field_errors}->{password_register}; my $body = JSON->new->utf8->allow_nonref->encode($return); $c->res->content_type('application/json; charset=utf-8'); @@ -488,22 +489,50 @@ sub test_password : Private { return 1 if $c->cobrand->call_hook('bypass_password_checks'); - my @errors; - + my $error; my $min_length = $c->cobrand->password_minimum_length; - push @errors, sprintf(_('Please make sure your password is at least %d characters long'), $min_length) - if length($password) < $min_length; - - push @errors, _('Please choose a less commonly-used password') - if found($password); + if (length($password) < $min_length) { + $error = sprintf(_('Please make sure your password is at least %d characters long'), $min_length); + } elsif (found($password)) { + $error = _('Please choose a less commonly-used password'); + } elsif (hibp($password)) { + $error = _('That password has appeared in a known third-party data breach (<a href="https://haveibeenpwned.com/Passwords" target="_blank">more information</a>); please choose another'); + } - if (@errors) { - $c->stash->{field_errors}->{password_register} = join('<br>', @errors); + if ($error) { + $c->stash->{field_errors}->{password_register} = $error; return 0; } return 1; } +=item hibp + +Returns true if we should check Have I Been Pwned and the check +comes back positive for a password that has been breached. + +=cut + +use Encode qw(encode); +use Digest::SHA qw(sha1_hex); +use LWP::Simple; +use Unicode::Normalize; + +sub hibp : Private { + my $password = shift; + + return 0 unless FixMyStreet->config('CHECK_HAVEIBEENPWNED'); + my $sha1 = uc sha1_hex(encode('UTF-8', NFD($password))); + my $url = 'https://api.pwnedpasswords.com/range/' . substr($sha1, 0, 5); + my $response = LWP::Simple::get($url); + my $remainder = substr($sha1, 5); + foreach my $line (split /\r\n/, $response) { + my ($part, $count) = split /:/, $line; + return $count if $part eq $remainder; + } + return 0; +} + =head2 sign_out Log the user out. Tell them we've done so. |