Currently, Commerce uses its own strategy for password hashing, based on different native PHP hashing algorithms. Commerce supports multiple algorithms like
Argon 2ID13. If the Sodium extension is installed (installed by default in PHP 7.3), then
Argon 2ID13 is chosen as the default hashing algorithm. Otherwise,
SHA256 is the default. Commerce can use the native PHP
password_hash function with Argon 2i algorithm support.
To avoid compromising older passwords that have been hashed with outdated algorithms like
MD5, the current implementation provides a method to upgrade the hash without changing the original password. In general, the password hash has the following format:
version<n> represents all the hash algorithms versions used on the password. Also, the salt is always stored together with the password hash, so we can restore the entire chain of algorithms. An example looks like:
The first part represents the password hash. The second,
8qnyO4H1OYIfGCUb is the salt. The last two are the different hash algorithms: 1 is
SHA256 and 2 is
Argon 2ID13. This means that the customer’s password was originally hashed with
SHA256 and after that, the algorithm was updated with
Argon 2ID13 and the hash was rehashed with Argon.
Consider what the hash upgrade mechanism looks like. Assume that originally, a password was hashed with
MD5 and then the algorithm was updated multiple times with Argon 2ID13. The following diagram shows the hash upgrade flow.
Each hash algorithm uses the previous password hash to generate a new hash. Commerce does not store the original, raw password.
As discussed above, the password hash might have multiple hash versions applied to the original password.
Here is how the password verification mechanism works during a customer authentication.
def verify(password, hash): restored = password hash_map = extract(hash) # iterate through all versions specified in the received hash [md5, sha256, argon2id13] for version in hash_map.get_versions(): # generate new hash based on password/previous hash, salt and version restored = hash_func(salt . restored, version) # extract only password hash from the hash:salt:version chain hash = hash_map.get_hash() return compare(restored, hash)
Since Commerce stores all used password hashes versions together with the password hash, we can restore the whole hash chain during the password verification. The hash verification mechanism is similar to the hash upgrade strategy: based on versions stored together with the password hash, the algorithm generates hashes from the provided password and returns the comparison result between hashed password and the database-stored hash.
\Magento\Framework\Encryption\Encryptor class is responsible for password hash generation and verification. The
bin/magento customer:hash:upgrade command upgrades a customer password hash to the latest hash algorithm.