aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App/Controller/Auth.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Auth.pm')
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm57
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.