Skip to content

Password Management

Yannick Warnier edited this page Mar 5, 2020 · 6 revisions

Note: This page is useful for all Chamilo 1.10.* and 1.11.* versions. Other versions might be affected by some slight differences. Older versions might not include the bcrypt option and actually be much easier to understand.

Every once in a while, we have to deal with passwords encryption and, because the password generation mechanism depends on deeply nested Symfony security code, we lose quite some time trying to remember how that stuff works.

For example, you might need to write a script that will generate new temporary passwords for your users, or you might want to check if passwords cannot be guessed too easily, for example comparing them to the string "12345678".

In order to do that, you need to understand how passwords work and how you can reproduce the hashing algorithm.

Just so you know, the final processing of a password is done, in Chamilo, through the MessageDigestPasswordEncoder::encodePassword() method, found in vendor/symfony/security/Core/Encoder/MessageDigestPasswordEncoder.php (just as expected, right?).

So it's not part of the Git code of Chamilo, and will only exist if you downloaded one of the stable packages, or executed the dependency manager "composer".

But if we know that, we don't need to retrace everything. All we need to know is that this method will depend on 3 "parameters" set in the constructor of the class:

  • the algorithm
  • whether to encode as base64
  • the number of iterations (because most encryption methods today decide on a number of times the password will be hashed, instead of just hashing it once)

This can be traced back to several points in the Chamilo code, like a call to UserManager::isPasswordValid() in main/auth/profile.php.

isPasswordValid() (and other related methods) will first get the encryption algorithm from app/config/configuration.php (setting 'password_encryption'), then send the raw password and the salt (a random string assigned to each user account and found in the database table "user") to src/Chamilo/UserBundle/Security/Encoder.php.

This same file uses (and this is an important point) the following classes:

use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder;

So depending on the hash algorithm you are using, one of these will be used. In our case, because bcrypt is the default, we will go with this one.

In this same Encoder.php file again, we find (in the constructor) that the "cost" for bcrypt (or the number of iterations, as it is called in other places) is set to 4 (hardcoded).

We also learn in BCryptPasswordEncoder::encodePassword() that the real hashing method, in the end, is:

return password_hash($raw, PASSWORD_BCRYPT, $options);

Where $options is, basically, the cost of "4" and the salt (combined in an array), and that "PASSWORD_BCRYPT" is a constant for the PHP function "password_hash()".

We have no further to go: if we want to generate a new password from a raw (clear) password contained in $raw and a salt string (obtained from the user table), we just need to do this:

$options['salt'] = $salt;
$options['cost'] = 4;
$password = password_hash($raw, PASSWORD_BCRYPT, $options);
Clone this wiki locally