-
Bug
-
Resolution: Fixed
-
Blocker
-
3.11, 4.1.4
-
MOODLE_311_STABLE, MOODLE_401_STABLE
-
MOODLE_403_STABLE
-
MDL-70371-master-rebased -
-
6
-
Team Hedgehog 2023 Review 2, Team Hedgehog 2023 Sprint 3.1, Team Hedgehog 2023 Sprint 3.2
We should mitigate any future risks of long password denial of service.
After doing a deep dive into how password resets work in our various authentication plugins, this does not appear to be a current security risk while bcrypt is used as the hashing algorithm.
All authentication plugins except for OAuth use moodlelib's hash_internal_user_password method, which utilises PHP's password_hash function using the default hashing algorithm. Currently, that is bcrypt, which (as mentioned in the docs) truncates passwords after 72 characters automatically and therefore effectively mitigates this risk. I confirmed this was the case in practice (both via forgot password and preferences).
I would suggest that we create a constant containing a reasonable upper limit (as is recommened by OWASP), which is applied as part of hash_internal_user_password(), so that when we change our hashing algorithm, it does not inadvertently introduce a login DoS vulnerability into Moodle. This will become critical once MDL-67390 lands, as SHA-512 does not perform the same truncating as bcrypt. We should also ensure that password peppers are factored in (introduced in MDL-67774), to ensure that peppers are not counted when validating the length of the password.
Regarding OAuth authentication plugin password resets, the hashing algorithms used are currently either MD5, SHA1 or none, and according to the same OWASP docs, using "faster" hashing algorithms to mitigate length concerns is a valid use case, therefore this does not appear to pose a denial of service vulnerability. I would suggest we also look into whether it would be suitable to update that with a limit while we are making these changes.
In addition to updating the limit in the backend, we also need to consider the following for any cases where the user can input, set or change their password:
- Updating the length limit on any password input fields.
- Applying frontend validation on length.
- Applying backend validation on length.
Currently the bcrypt truncation occurs silently, so none of the above have been implemented in the existing functionality.
I have marked this as an improvement, but think we should consider backporting some or all of this to any relevant security branches, if it does not raise any breaking changes/blockers that would prevent it.
Writeup/testing/investigation by michaelh, after receiving an initial report re potential login DoS on forgot password reset by Royniss8990 (SF case 00091645).
Initial test results:
Some quick test results for various hashing methods. The first set of methods use SHA512 with 10,000 rounds to generate a single password of varying lengths:
Length: 8, Time: 0.005295991897583 seconds |
Length: 16, Time: 0.0082888603210449 seconds |
Length: 25, Time: 0.0095119476318359 seconds |
Length: 50, Time: 0.0093979835510254 seconds |
Length: 100, Time: 0.016441106796265 seconds |
Length: 200, Time: 0.017743825912476 seconds |
Length: 300, Time: 0.033573865890503 seconds |
Length: 400, Time: 0.037233114242554 seconds |
Length: 500, Time: 0.043007135391235 seconds |
Length: 600, Time: 0.049973011016846 seconds |
Length: 700, Time: 0.081401109695435 seconds |
Length: 800, Time: 0.066431999206543 seconds |
Length: 900, Time: 0.10497498512268 seconds |
Length: 1000, Time: 0.099220991134644 seconds |
The second tests compare the time to generate 1,000 hashes for randomly generated 1,000 digit passwords
Generating 1000 hashes using bcrypt... |
Cost: 4, Time: 1.110445022583 seconds |
Cost: 10, Time: 68.429219961166 seconds |
As expected Bcrypt hashing times for passwords of length 1000 are more or less the same for smaller lengths (because of the 72 digit cut off).
However, the SHA2 class of hashes do take an extended period of time with long passwords.
Generating 1000 hashes using SHA256... |
Rounds: 5000, Time: 138.83062696457 seconds |
Rounds: 10000, Time: 256.1211771965 seconds |
Generating 1000 hashes using SHA512... |
Rounds: 5000, Time: 92.857749223709 seconds |
Rounds: 10000, Time: 164.08723402023 seconds |
Therefore when when we change to SHA512 for password hashes, we should limit the maximum password length. A max length of 128 characters seems a good compromise. It exceeds NIST (as per NIST Special Publication 800-63B) recommendation, and allows for multi word passphrases.
Rerunning the test to generate 1000 passwords hashes using 128 character random passwords using SHA512 achieves acceptable results.
Generating 1000 hashes using SHA512... |
Rounds: 5000, Time: 12.569887876511 seconds |
Rounds: 10000, Time: 25.713526010513 seconds |
- is blocked by
-
MDL-67774 Specify password peppers in config.php
- Closed