Is it possible to add a salt to passwords in .hpasswd files? I assume not since the server would need the salt for each user in order to verify the password and I can't think of how it would get them, but otherwise if the list was to be obtained it would be rather vulnerable. Is there a solution?
Many thanks for your help, Ben
By default htpasswd uses the standard crypt function and thus passwords are already salted - note in this example that both users have the same password yet the hashes are different:
simon@diablo:~$ htpasswd -b -c htpasswd simon abcd Adding password for user simon simon@diablo:~$ htpasswd -b htpasswd simon2 abcd Adding password for user simon2 simon@diablo:~$ cat htpasswd simon:NWvm/LCCxQ64E simon2:2I.LBzsRqULN6
(note: the -b
flag is normally discouraged because other users can see your command line arguments and hence the password)
The first two characters of the hash are the salt; passwords are verified by calling crypt()
again. Entering the wrong password produces a string that's unequal to the hashed password:
>>> from crypt import crypt
>>> crypt("wrongpass", "NWvm/LCCxQ64E")
'NWbxQgX1unvso'
whereas the correct password produces the expected hash:
>>> crypt("abcd", "NWvm/LCCxQ64E")
'NWvm/LCCxQ64E'
htpasswd -m
uses a different algorithm that's MD5-based and uses a longer salt:
simon@diablo:~$ htpasswd -m -b -c htpasswd simon abcd Adding password for user simon simon@diablo:~$ cat htpasswd simon:$apr1$mfvnBVmG$iIHIHOaH9vcImG5G.8eVa/
Here, the salt is the 8 characters between the second and third $
.
htpasswd -s
stores a SHA-1 digest with no salt; this appears to be for compatibility with Netscape/LDIF:
simon@diablo:~$ htpasswd -s -b -c htpasswd simon abcd Adding password for user simon simon@diablo:~$ htpasswd -s -b htpasswd simon2 abcd Adding password for user simon2 simon@diablo:~$ cat htpasswd simon:{SHA}gf6L/odXbD7LIkJvjleEc4KRes8= simon2:{SHA}gf6L/odXbD7LIkJvjleEc4KRes8=
These can easily be reversed - convert into a hex digest:
>>> "".join("%02x" % ord(c)
... for c in "gf6L/odXbD7LIkJvjleEc4KRes8=".decode("base64"))
'81fe8bfe87576c3ecb22426f8e57847382917acf'
then use an online hash database.