Passwords: check in constant time

The default password comparison logic does not use
constant time validation. Switching to constant time
offers a meager improvement by removing a timing
oracle.

A prepatory step in moving to Argon2id password storage, since we'll need this change anyway after
for validating existing passwords.

Co-authored-by: Graham Christensen <graham@grahamc.com>
This commit is contained in:
Graham Christensen 2021-04-15 10:40:55 -04:00 committed by Graham Christensen
parent d4d8f1ba1b
commit 29620df85e
4 changed files with 53 additions and 2 deletions

View file

@ -229,6 +229,19 @@
license = with final.stdenv.lib.licenses; [ artistic1 ]; license = with final.stdenv.lib.licenses; [ artistic1 ];
}; };
}; };
StringCompareConstantTime = final.buildPerlPackage {
pname = "String-Compare-ConstantTime";
version = "0.321";
src = final.fetchurl {
url = "mirror://cpan/authors/id/F/FR/FRACTAL/String-Compare-ConstantTime-0.321.tar.gz";
sha256 = "0b26ba2b121d8004425d4485d1d46f59001c83763aa26624dff6220d7735d7f7";
};
meta = {
description = "Timing side-channel protected string compare";
license = with final.lib.licenses; [ artistic1 gpl1Plus ];
};
};
}; };
hydra = with final; let hydra = with final; let
@ -279,6 +292,7 @@
SQLSplitStatement SQLSplitStatement
SetScalar SetScalar
Starman Starman
StringCompareConstantTime
SysHostnameLong SysHostnameLong
TermSizeAny TermSizeAny
TestMore TestMore

View file

@ -34,8 +34,7 @@ __PACKAGE__->config(
credential => { credential => {
class => "Password", class => "Password",
password_field => "password", password_field => "password",
password_type => "hashed", password_type => "self_check",
password_hash_type => "SHA-1",
}, },
store => { store => {
class => "DBIx::Class", class => "DBIx::Class",

View file

@ -195,6 +195,9 @@ __PACKAGE__->many_to_many("projects", "projectmembers", "project");
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-02-06 12:22:36 # Created by DBIx::Class::Schema::Loader v0.07049 @ 2020-02-06 12:22:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:4/WZ95asbnGmK+nEHb4sLQ # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:4/WZ95asbnGmK+nEHb4sLQ
use Digest::SHA1 qw(sha1_hex);
use String::Compare::ConstantTime;
my %hint = ( my %hint = (
columns => [ columns => [
"fullname", "fullname",
@ -210,4 +213,10 @@ sub json_hint {
return \%hint; return \%hint;
} }
sub check_password {
my ($self, $password) = @_;
return String::Compare::ConstantTime::equals($self->password, sha1_hex($password));
}
1; 1;

29
t/Schema/Users.t Normal file
View file

@ -0,0 +1,29 @@
use strict;
use Setup;
my %ctx = test_init();
require Hydra::Schema;
require Hydra::Model::DB;
use Test2::V0;
my $db = Hydra::Model::DB->new;
hydra_setup($db);
# Catalyst's default password checking is not constant time. To improve
# the security of the system, we replaced the check password routine.
# Verify comparing correct and incorrect passwords work.
# Starting the user with a sha1 password
my $user = $db->resultset('Users')->create({
"username" => "alice",
"emailaddress" => 'alice@nixos.org',
"password" => "8843d7f92416211de9ebb963ff4ce28125932878" # SHA1 of "foobar"
});
isnt($user, undef, "My user was created.");
ok(!$user->check_password("barbaz"), "Checking the password, barbaz, is not right");
ok($user->check_password("foobar"), "Checking the password, foobar, is right");
done_testing;