Upgrade Password Hashing Method for Existing Users
When you’ve been using the same Linux system for a while, you might want to upgrade the password hashing methods used. This article explains how to discover the methods supported by your system, set the preferred method and then upgrade the password hashes to make use of the new hashing method.
You can see the supported hashing methods by looking at man 5 crypt
. (Note that man
section 5
indicates “File
formats and conversions, e.g. /etc/passwd”.) Currently, on
my system, yescrypt
is favored and recommended for new hashes.
To check your current hashing method on somebody’s account:
# getent shadow john
john:$6$JYJjfxe2YGsIGlvz$7bDibTmj/Iq.EelFEBJd24Ip66fVeBLGK/CMUyOl/9YpgIxtDfq2cLzzAc5b7sXhW3nCc2f4lKzMejFrBr3UF/:18133::::::
(You can read about the format of shadow
by looking at man 5 shadow
.) Again, looking at man 5 crypt
, we
see that the $6$
in the second section after the first colon :
indicates that this password was hashed with sha512crypt
which,
according to the same documentation is acceptable for new hashes. But we
know we can do better by using a “memory-hard” hash such as yescrypt
.
passwd(1)
changes user passwords. It will use the
hashing method specified by the pam_unix.so
module in
the PAM configuration (/etc/pam.d/passwd
) or else look at the
ENCRYPT_METHOD
variable in /etc/login.defs
:
$ grep ^password /etc/pam.d/passwd
password include system-auth
$ grep ^password /etc/pam.d/system-auth
password required pam_unix.so try_first_pass nullok shadow
password optional pam_permit.so
(Information about pam_unix.so
arguments can be found in man 8 pam_unix
.)
Here, we see that nothing explicit is said about which hashing method
should be used for passwords. So we turn to
/etc/login.defs
:
$ grep ^ENCRYPT_METHOD /etc/login.defs
ENCRYPT_METHOD YESCRYPT
Now, when we upgrade our password with passwd
, it will be protected by
the yescrypt
($y$
) hashing method:
# getent shadow john
john:$y$j9T$8Z3MNrSUYkSPZWcm83Dh$p.Qev5ogCpuF.KfGBjw1QfBBiV7m05PPWxIGuNtbJc9:19793:0:99999:7:::
You can create a yescrypt
password hash yourself by running:
$ export PASS=pass SALT='$y$j9T$8Z3MNrSUYkSPZWcm83Dh$'
$ perl -le 'print crypt($ENV{PASS}, $ENV{SALT})'
$y$j9T$8Z3MNrSUYkSPZWcm83Dh$p.Qev5ogCpuF.KfGBjw1QfBBiV7m05PPWxIGuNtbJc9
You can read more about the format of the input to
crypt(3)
in man 5 crypt
under “FORMAT OF
HASHED PASSPHRASES”. Suffice to say here that it consists of a prefix
,
options
, a salt
and a hash
, separated by $
. The prefix
field
indicates which has is to be used and changes what the other fields
mean.
In this current example, I do not know the exact requirements for the
salt
, for instance. I experimented and found that openssl rand -base64 15
produces a value that
crypt(3)
grokked.
(Incidentally, password verification is made easy, because you can just
compare the result of crypt(3)
with the password hash as
stored.)
The yescrypt
hashing method takes a cost factor. This can be specified
in /etc/login.defs
under the YESCRYPT_COST_FACTOR
key. Unlike the KDF rounds used with the sha256crypt
and sha512crypt
methods, which increases complexity linearly, increasing the cost factor
of yescrypt
will lead to an exponential increase in memory usage and
slow the password comparison down significantly. You might want to
experiment on your system and see what the highest setting is you still
find bearable. Although you might not want to wait 5 seconds each time
you log in or want to use sudo
, keep in mind that it will take
somebody that cracks these the same amount of resources for each guess.
See yescrypt
parameters (especially the N
parameter which is influenced by the YESCRYPT_COST_FACTOR
key) and a
description of them on the Unix & Linux Stack Exchange.