Skip to content

Commit

Permalink
Create a utf8Crypt method to handle UTF-8 encoded password hashing.
Browse files Browse the repository at this point in the history
This makes it easy to generate a password hash or verify a given
password against a previously generated password hash.

Note that this uses Encode::encode (via the Mojo::Util encode method)
rather than Encode::utf8_encode.  The Encode::utf8_encode method should
not be used anymore.
  • Loading branch information
drgrice1 committed Jan 17, 2025
1 parent 5a81a74 commit 1e0ee36
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 26 deletions.
13 changes: 3 additions & 10 deletions lib/WeBWorK/Authen.pm
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use Scalar::Util qw(weaken);
use Mojo::Util qw(b64_encode b64_decode);

use WeBWorK::Debug;
use WeBWorK::Utils qw(x runtime_use);
use WeBWorK::Utils qw(x runtime_use utf8Crypt);
use WeBWorK::Utils::Logs qw(writeCourseLog);
use WeBWorK::Utils::TOTP;
use WeBWorK::Localize;
Expand Down Expand Up @@ -633,15 +633,8 @@ sub checkPassword {
my $Password = $db->getPassword($userID);
if (defined $Password) {
# Check against the password in the database.
my $possibleCryptPassword = '';
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
# If crypt fails due to a wide character, encode to UTF-8 before calling crypt.
eval { $possibleCryptPassword = crypt $possibleClearPassword, $Password->password; };
if ($@ && $@ =~ /Wide char/) {
$possibleCryptPassword = crypt Encode::encode_utf8($possibleClearPassword), $Password->password;
}

my $dbPassword = $Password->password;
my $possibleCryptPassword = utf8Crypt($possibleClearPassword, $Password->password);
my $dbPassword = $Password->password;
# This next line explicitly insures that blank or null passwords from the database can never succeed in matching
# an entered password. This also rejects cases when the database has a crypted password which matches a
# submitted all white-space or null password by requiring that the $possibleClearPassword contain some non-space
Expand Down
13 changes: 2 additions & 11 deletions lib/WeBWorK/ContentGenerator/Options.pm
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ WeBWorK::ContentGenerator::Options - Change user options.
=cut

use WeBWorK::Utils qw(cryptPassword);
use WeBWorK::Utils qw(cryptPassword utf8Crypt);
use WeBWorK::Localize;

sub page_title ($c) {
Expand Down Expand Up @@ -56,16 +56,7 @@ sub initialize ($c) {
$userID ne $effectiveUserID ? eval { $db->getPassword($c->{effectiveUser}->user_id) } : $password;

# Check that either password is not defined or if it is defined then we have the right one.
my $cryptedCurrP;
if (defined $password) {
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
# If crypt fails due to a wide character, encode to UTF-8 before calling crypt.
eval { $cryptedCurrP = crypt($currP // '', $password->password); };
if ($@ && $@ =~ /Wide char/) {
$cryptedCurrP = crypt(Encode::encode_utf8($currP), $password->password);
}
}
if (!defined $password || $cryptedCurrP eq $password->password) {
if (!defined $password || utf8Crypt($currP // '', $password->password) eq $password->password) {
my $e_user_name = $c->{effectiveUser}->first_name . ' ' . $c->{effectiveUser}->last_name;
if ($newP eq $confirmP) {
if (!defined $effectiveUserPassword) {
Expand Down
22 changes: 17 additions & 5 deletions lib/WeBWorK/Utils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ our @EXPORT_OK = qw(
max
wwRound
cryptPassword
utf8Crypt
undefstr
sortByName
sortAchievements
Expand Down Expand Up @@ -151,14 +152,15 @@ sub cryptPassword ($clearPassword) {
$salt .= ('.', '/', '0' .. '9', 'A' .. 'Z', 'a' .. 'z')[ rand 64 ];
}

return utf8Crypt(trim_spaces($clearPassword), $salt);
}

sub utf8Crypt ($clearPassword, $hash) {
# Wrap crypt in an eval to catch any "Wide character in crypt" errors.
# If crypt fails due to a wide character, encode to UTF-8 before calling crypt.
my $cryptedPassword = '';
eval { $cryptedPassword = crypt(trim_spaces($clearPassword), $salt); };
if ($@ && $@ =~ /Wide char/) {
$cryptedPassword = crypt(Encode::encode_utf8(trim_spaces($clearPassword)), $salt);
}

eval { $cryptedPassword = crypt($clearPassword, $hash); };
$cryptedPassword = crypt(encode('UTF-8', $clearPassword), $hash) if $@ && $@ =~ /Wide char/;
return $cryptedPassword;
}

Expand Down Expand Up @@ -621,6 +623,16 @@ Usage: C<cryptPassword($clearPassword)>
Returns the crypted form of C<$clearPassword> using a random 16 character
salt.
=head2 utf8Crypt
Usage: C<utf8Crypt($clearPassword, $hash)>
Attempts to call C<crypt> on C<$clearPassword>. If that fails, then C<crypt> is
called on the UTF-8 encoded version of C<$clearPassword>.
Note that C<$hash> can be a salt or a password hash generated by a previous call
of this method with a salt..
=head2 undefstr
Usage: C<undefstr($default, @values)>
Expand Down

0 comments on commit 1e0ee36

Please sign in to comment.