From 766f0ed07864ac89a81d9356a8960c3c0744fc99 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 1 Jun 2020 10:47:27 +0100 Subject: [fixmystreet.com] Add option for recaptcha. We are getting spam submissions of e.g. alert subscribe form, which causes people to get a confirmation email. --- perllib/FixMyStreet/App/Controller/Auth.pm | 3 +++ 1 file changed, 3 insertions(+) (limited to 'perllib/FixMyStreet/App/Controller/Auth.pm') diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index cecfa318c..beba6b235 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 { -- cgit v1.2.3 From 9a12c0dac0b7677938f33f5abb639a296adff9c5 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 25 Jun 2020 16:23:33 +0100 Subject: Add option to check password on Have I Been Pwned. If switched on, sends first five letters of the SHA1 hash of the entered password to HIBP's API, which then returns all matching hashes in their database of breached passwords. If we find a match, tell the user they need to pick a different password. --- perllib/FixMyStreet/App/Controller/Auth.pm | 54 ++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) (limited to 'perllib/FixMyStreet/App/Controller/Auth.pm') diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index beba6b235..0d0c2240a 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -460,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 @@ -469,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'); @@ -491,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 (more information); please choose another'); + } - if (@errors) { - $c->stash->{field_errors}->{password_register} = join('
', @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. -- cgit v1.2.3