forked from lix-project/hydra
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:
parent
d4d8f1ba1b
commit
29620df85e
14
flake.nix
14
flake.nix
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
29
t/Schema/Users.t
Normal 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;
|
Loading…
Reference in a new issue