Comparison of password hashing algorithms in common open source PHP-based e-commerce solutions

Jaroslav Tesař
Dr.Max Product Blog
6 min readDec 15, 2021

Every website or application that handles sensitive data provides some form of authentication, such as verification of the name and password entered. Therefore, there is always a need to store this data in some way in the database so that it can be authenticated on request and allow the user into a protected part of the application. However, storing passwords on the server-side for authentication is a difficult task. Let’s explore one of the mechanisms that make password storage secure and easier: hashing.

Do you remember the great Twitter data breach 2018 based on the glitch that stored passwords unmasked in an internal log, making all user passwords accessible to the internal network? Yahoo data breach in 2017 where a group of hackers had compromised 1 billion accounts. In this instance, security questions and answers were also compromised, increasing the risk of identity theft. LinkedIn data breach — 165 million user accounts had been compromised, including 117 million passwords that had been hashed but not “salted” with random data to make them harder to reverse. Or the Dropbox data breach in mid-2012 which exposed 68 million records that contained email addresses and salted hashes of passwords?

All of the above have one thing in common — user data was leaked, mostly including hashed passwords.

Such an incident happens from time to time, and it is all the more painful the larger or more widespread the affected application is. Imagine if the passwords were stored in a plaintext database.

According to Cornell, plaintext refers to data that will serve as the input to a cryptographic algorithm

With the fact that password reuse remains rampant, with 53 percent of people admitting they use the same password for different accounts (according to a report by identity company SecureAuth) there is a good chance that the recovered password will open yet another door.

Hashing is the foundation of secure password storage, it is not a reversible operation, the password cannot be recovered. It is called a one-way function, that is, a function for which it is practically infeasible to invert or reverse the computation. To authenticate a user, the password presented by the user is hashed and compared with the stored hash. A password hash requires the use of a large random, non-secret salt value which can be stored with the password hash. The salt randomizes the output of the password hash, making it impossible for an adversary to store tables of passwords and precomputed hash values to which the password hash digest can be compared. The password can be cracked though and to make cracking as hard as possible we have to use a secure algorithm.

You may be thinking that it is no longer necessary to deal with the issue of storing passwords in databases. No website stores hashed passwords in a database using cryptographically broken functions anymore, right? Let’s find out.

For this article, I have chosen the following open source e-commerce platforms based on PHP.

  • Magento 2
  • WooCommerce — the WordPress plug-in
  • PrestaShop
  • OpenCart

Magento 2

If the Sodium extension is installed (installed by default in PHP 7.3), then Argon 2ID13 will be chosen as the default hashing algorithm. Otherwise, SHA256 will be the default. Magento can use the native PHP password_hash function with Argon 2i algorithm support. The authors of Magento have also thought about the possible change of the hash algorithm over time, so after the platform update, it is possible to update the hash without the need for the user’s intervention. On top of that, the salt is generated individually for each customer, there is no global salt. It’s generated when the password is hashed, in \Magento\Framework\Encryption\Encryptor::getHash().

To summarize:

  • Argon2 is a cryptographic hash algorithm specifically designed to secure passwords; it is adaptable to the future CPU’s performance increase. It is recommended by OWASP in the Argon2id variant as a modern, secure and flexible algorithm.
  • A strong but fast hashing algorithm as the default (SHA256) is not so ideal, simply because it’s fast — the main point is to choose slow algorithms to avoid fast brute-force breaking.
  • Individual salt for every password hash (random salt per user prevents the use of reverse lookup tables).
  • Updatable hash in case a new algorithm is present.

WooCommerce

You may have heard at some point that WordPress uses MD5. Is that still true? If we look at the crypt_private function in the PasswordHash class, we can see that it is. However, the MD5 function is used repeatedly when a randomized salt is used and the encode64 function is used at the end. But is that enough? It is known that the security of the MD5 hash function is severely compromised. There are so-called precomputed rainbow tables that can be used to retrieve the original text (password), similar to this article where the author managed to crack the WordPress hash quite easily.

  • No or insufficient documentation of heavily used framework with infrequent updates (WordPress takes first place in the category of open source projects on the whole Internet with an unbeatable 80% representation).
  • Unique salt for each password unfortunately using a cracked algorithm not recommended for password hashing since 1996.

PrestaShop

PrestaShop — another heavily used OpenSource e-commerce platform that is currently used by 300,000 shops worldwide. Although the technical documentation for this project is not bad, it doesn’t mention anything about how passwords are stored or hashed. So it comes back to the code itself. The function responsible for this can be found in the Tools.php class. It looks like this:

/**
* Hash password.
* @param string $passwd String to has
* @return string Hashed password
* @since 1.7.0
*/
public static function hash($passwd) {
return md5(_COOKIE_KEY_ . $passwd);
}

The _COOKIE_KEY_ is just a string used as a salt. It can be found in app/config/parameters.php file and default value is ThisTokenIsNotSoSecretChangeIt. As already mentioned, the MD5 algorithm is no longer considered secure for password hashing, and just adding a random string (in this case even the same one for all passwords in the database) will not change that.

OpenCart

This PHP-based online store management system is only slightly more used than the previous PrestaShop. Unfortunately, the documentation does not reveal the secret of password hashing so we have to move on to the code again.

The current stable version available for download is v3.0.3.8. If we look at the way passwords are stored, for example in the file upload/catalog/model/account/customer.php we can see this piece of code:

$this->db->escape(sha1($salt . sha1($salt . sha1($password))))

And as Michal Špaček (the very well know security engineer in the Czech Republic) in this Tweet thread says, random numbers too are not secure either! (the random number is used as a salt in OpenCart editPassword function) And worse: “OpenCart hashes the password in the database so it sends it in clear to the DB server and the plaintext might end up in logs”. What I really don’t understand is this long time ago closed pull request, which partially fixes the security hole by replacing the SHA1 algorithm with the password_hash function. The current stable version does not contain these modifications! And it was merged 4 years ago!

Coincidentally, SHA1 was also declared dead 4 years ago. It falls to the first-known collision attack.

What to say in conclusion.

If you’re a developer, make sure you’re using updated tools that keep track of what’s happening in the security field. As you can see, the frequency of use of a framework may not have any positive effect on its security despite frequent requests from contributors. If you’re a user (and we all are) a password made up of your pet’s name and your child’s birth year isn’t exactly secure, and that’s because it’s almost certainly not unique. Try to at least approach password uniqueness by using combinations of at least numbers, letters, and various characters. Let’s give hackers a hard time!

--

--