forked from lix-project/hydra
Users: transparently upgrade passwords to Argon2
Passwords that are sha1 will be transparently upgraded to argon2, and future comparisons will use Argon2 Co-authored-by: Graham Christensen <graham@grahamc.com>
This commit is contained in:
parent
29620df85e
commit
1da70030b7
43
flake.nix
43
flake.nix
|
@ -70,6 +70,47 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CryptArgon2 = final.perlPackages.buildPerlModule {
|
||||||
|
pname = "Crypt-Argon2";
|
||||||
|
version = "0.010";
|
||||||
|
src = final.fetchurl {
|
||||||
|
url = "mirror://cpan/authors/id/L/LE/LEONT/Crypt-Argon2-0.010.tar.gz";
|
||||||
|
sha256 = "3ea1c006f10ef66fd417e502a569df15c4cc1c776b084e35639751c41ce6671a";
|
||||||
|
};
|
||||||
|
nativeBuildInputs = [ pkgs.ld-is-cc-hook ];
|
||||||
|
meta = {
|
||||||
|
description = "Perl interface to the Argon2 key derivation functions";
|
||||||
|
license = final.lib.licenses.cc0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
CryptPassphrase = final.buildPerlPackage {
|
||||||
|
pname = "Crypt-Passphrase";
|
||||||
|
version = "0.003";
|
||||||
|
src = final.fetchurl {
|
||||||
|
url = "mirror://cpan/authors/id/L/LE/LEONT/Crypt-Passphrase-0.003.tar.gz";
|
||||||
|
sha256 = "685aa090f8179a86d6896212ccf8ccfde7a79cce857199bb14e2277a10d240ad";
|
||||||
|
};
|
||||||
|
meta = {
|
||||||
|
description = "A module for managing passwords in a cryptographically agile manner";
|
||||||
|
license = with final.lib.licenses; [ artistic1 gpl1Plus ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
CryptPassphraseArgon2 = final.buildPerlPackage {
|
||||||
|
pname = "Crypt-Passphrase-Argon2";
|
||||||
|
version = "0.002";
|
||||||
|
src = final.fetchurl {
|
||||||
|
url = "mirror://cpan/authors/id/L/LE/LEONT/Crypt-Passphrase-Argon2-0.002.tar.gz";
|
||||||
|
sha256 = "3906ff81697d13804ee21bd5ab78ffb1c4408b4822ce020e92ecf4737ba1f3a8";
|
||||||
|
};
|
||||||
|
propagatedBuildInputs = with final.perlPackages; [ CryptArgon2 CryptPassphrase ];
|
||||||
|
meta = {
|
||||||
|
description = "An Argon2 encoder for Crypt::Passphrase";
|
||||||
|
license = with final.lib.licenses; [ artistic1 gpl1Plus ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
DirSelf = final.buildPerlPackage {
|
DirSelf = final.buildPerlPackage {
|
||||||
pname = "Dir-Self";
|
pname = "Dir-Self";
|
||||||
version = "0.11";
|
version = "0.11";
|
||||||
|
@ -267,6 +308,8 @@
|
||||||
CatalystViewTT
|
CatalystViewTT
|
||||||
CatalystXScriptServerStarman
|
CatalystXScriptServerStarman
|
||||||
CatalystXRoleApplicator
|
CatalystXRoleApplicator
|
||||||
|
CryptPassphrase
|
||||||
|
CryptPassphraseArgon2
|
||||||
CryptRandPasswd
|
CryptRandPasswd
|
||||||
DBDPg
|
DBDPg
|
||||||
DBDSQLite
|
DBDSQLite
|
||||||
|
|
|
@ -195,6 +195,7 @@ __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 Crypt::Passphrase;
|
||||||
use Digest::SHA1 qw(sha1_hex);
|
use Digest::SHA1 qw(sha1_hex);
|
||||||
use String::Compare::ConstantTime;
|
use String::Compare::ConstantTime;
|
||||||
|
|
||||||
|
@ -216,7 +217,28 @@ sub json_hint {
|
||||||
sub check_password {
|
sub check_password {
|
||||||
my ($self, $password) = @_;
|
my ($self, $password) = @_;
|
||||||
|
|
||||||
return String::Compare::ConstantTime::equals($self->password, sha1_hex($password));
|
my $authenticator = Crypt::Passphrase->new(
|
||||||
|
encoder => 'Argon2',
|
||||||
|
validators => [
|
||||||
|
(sub {
|
||||||
|
my ($password, $hash) = @_;
|
||||||
|
|
||||||
|
return String::Compare::ConstantTime::equals($hash, sha1_hex($password));
|
||||||
|
})
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($authenticator->verify_password($password, $self->password)) {
|
||||||
|
if ($authenticator->needs_rehash($self->password)) {
|
||||||
|
$self->update({
|
||||||
|
"password" => $authenticator->hash_password($password),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -11,11 +11,20 @@ use Test2::V0;
|
||||||
my $db = Hydra::Model::DB->new;
|
my $db = Hydra::Model::DB->new;
|
||||||
hydra_setup($db);
|
hydra_setup($db);
|
||||||
|
|
||||||
# Catalyst's default password checking is not constant time. To improve
|
# Hydra used to store passwords, by default, as plain unsalted sha1 hashes.
|
||||||
# the security of the system, we replaced the check password routine.
|
# We now upgrade these badly stored passwords with much stronger algorithms
|
||||||
# Verify comparing correct and incorrect passwords work.
|
# when the user logs in. Implementing this meant reimplementing our password
|
||||||
|
# checking ourselves, so also ensure that basic password checking works.
|
||||||
|
#
|
||||||
|
# This test:
|
||||||
|
#
|
||||||
|
# 1. creates a user with the legacy password
|
||||||
|
# 2. validates that the wrong password is not considered valid
|
||||||
|
# 3. validates that the correct password is valid
|
||||||
|
# 4. checks that the checking of the correct password transparently upgraded
|
||||||
|
# the password's storage to a more secure algorithm.
|
||||||
|
|
||||||
# Starting the user with a sha1 password
|
# Starting the user with an unsalted sha1 password
|
||||||
my $user = $db->resultset('Users')->create({
|
my $user = $db->resultset('Users')->create({
|
||||||
"username" => "alice",
|
"username" => "alice",
|
||||||
"emailaddress" => 'alice@nixos.org',
|
"emailaddress" => 'alice@nixos.org',
|
||||||
|
@ -24,6 +33,10 @@ my $user = $db->resultset('Users')->create({
|
||||||
isnt($user, undef, "My user was created.");
|
isnt($user, undef, "My user was created.");
|
||||||
|
|
||||||
ok(!$user->check_password("barbaz"), "Checking the password, barbaz, is not right");
|
ok(!$user->check_password("barbaz"), "Checking the password, barbaz, is not right");
|
||||||
|
|
||||||
|
is($user->password, "8843d7f92416211de9ebb963ff4ce28125932878", "The unsalted sha1 is in the database.");
|
||||||
ok($user->check_password("foobar"), "Checking the password, foobar, is right");
|
ok($user->check_password("foobar"), "Checking the password, foobar, is right");
|
||||||
|
isnt($user->password, "8843d7f92416211de9ebb963ff4ce28125932878", "The user has had their password rehashed.");
|
||||||
|
ok($user->check_password("foobar"), "Checking the password, foobar, is still right");
|
||||||
|
|
||||||
done_testing;
|
done_testing;
|
||||||
|
|
Loading…
Reference in a new issue