Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-70371

Define a password length upper limit to future-proof from hashing denial of service risks

XMLWordPrintable

    • MOODLE_311_STABLE, MOODLE_401_STABLE
    • MOODLE_403_STABLE
    • MDL-70371-master-rebased
    • Hide

      Testing

      1. Add a new user page (should be the same for Update profile page)

      1. Login as admin
      2. Go to Site admin > Users > Add a new user
      3. Fill all the required fields as you want but for the New password please enter "thisisapasswordthatcontainscharactersthatcanexeedthepasswordlengthof128thisispasswordthatcontainscharactersthatcanexeedthelength-----"
      4. Click on the eye icon and go the end of the password
      5. Verify that the last five dasshes "-----" have been removed
      6. Open browser dev tool and remove the maxlength attribute and its value of the New password input
      7. Enter the same password step #3
      8. Verify that you could not set the password due to the password being longer then 128 characters
      9. Repeat all the steps from step #1 but this time set the New password to something less than 128 characters
      10. Verify that you successfully created a new user

      2. Forgot password page

      1. Make sure you have a mail server like mailpit and run it
      2. Go to login page and click on Lost password?
      3. Put a username in Search by username then press Search
      4. Check your mailpit inbox and visit the URL sent within the email
      5. Open browser dev tool and remove the maxlength attribute and its value for both input New password and New password (again):
      6. Enter "thisisapasswordthatcontainscharactersthatcanexeedthepasswordlengthof128thisispasswordthatcontainscharactersthatcanexeedthelength-----" in New password and New password (again) fields
      7. Click Save changes
      8. Verify that you could not set the password due to the password being longer then 128 characters
      9. Click on Continue
      10. Repeat the steps, but in step #8 you need to remove 5 trailing dashes
      11. After step #9, Verify that you successfully set a new password

      3. Signup page

      1. Make sure you enabled Email-based self-registration in Site admin > Plugins > Authentication > Manage authentication
      2. Scroll down to Common settings section
      3. Set Self registration to "Email-based self-registration" then Save changes
      4. Logout or open a new tab then click on the Create new account
      5. Open browser dev tool and remove the maxlength attribute and its value of the Password input
      6. Copy and paste "thisisapasswordthatcontainscharactersthatcanexeedthepasswordlengthof128thisispasswordthatcontainscharactersthatcanexeedthelength-----" for couple of times so that you get more than 128 characters into Password field
      7. Click Create my new account
      8. Verify that you could not set the password due to the password being longer then 128 characters

      You can also test the above pages with peppers as well and for the config you can have a look at MDL-67774

      Show
      Testing 1. Add a new user page (should be the same for Update profile page) Login as admin Go to Site admin > Users > Add a new user Fill all the required fields as you want but for the New password please enter "thisisapasswordthatcontainscharactersthatcanexeedthepasswordlengthof128thisispasswordthatcontainscharactersthatcanexeedthelength-----" Click on the eye icon and go the end of the password Verify that the last five dasshes "-----" have been removed Open browser dev tool and remove the maxlength attribute and its value of the New password input Enter the same password step #3 Verify that you could not set the password due to the password being longer then 128 characters Repeat all the steps from step #1 but this time set the New password to something less than 128 characters Verify that you successfully created a new user 2. Forgot password page Make sure you have a mail server like mailpit and run it Go to login page and click on Lost password? Put a username in Search by username then press Search Check your mailpit inbox and visit the URL sent within the email Open browser dev tool and remove the maxlength attribute and its value for both input New password and New password (again) : Enter "thisisapasswordthatcontainscharactersthatcanexeedthepasswordlengthof128thisispasswordthatcontainscharactersthatcanexeedthelength-----" in New password and New password (again) fields Click Save changes Verify that you could not set the password due to the password being longer then 128 characters Click on Continue Repeat the steps, but in step #8 you need to remove 5 trailing dashes After step #9, Verify that you successfully set a new password 3. Signup page Make sure you enabled Email-based self-registration in Site admin > Plugins > Authentication > Manage authentication Scroll down to Common settings section Set Self registration to "Email-based self-registration" then Save changes Logout or open a new tab then click on the Create new account Open browser dev tool and remove the maxlength attribute and its value of the Password input Copy and paste "thisisapasswordthatcontainscharactersthatcanexeedthepasswordlengthof128thisispasswordthatcontainscharactersthatcanexeedthelength-----" for couple of times so that you get more than 128 characters into Password field Click Create my new account Verify that you could not set the password due to the password being longer then 128 characters You can also test the above pages with peppers as well and for the config you can have a look at MDL-67774
    • 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

       

        1. 2023-08-29_09-15.png
          2023-08-29_09-15.png
          16 kB
        2. image-2023-08-29-09-29-15-115.png
          image-2023-08-29-09-29-15-115.png
          58 kB
        3. MDL-70371-Add a new user page.png
          MDL-70371-Add a new user page.png
          1.64 MB
        4. MDL-70371-Forgot password page.png
          MDL-70371-Forgot password page.png
          1.42 MB
        5. MDL-70371-Signup page.png
          MDL-70371-Signup page.png
          740 kB
        6. test_script.php
          2 kB
        7. test2_script.php
          1 kB

            stevani.andolo@moodle.com Stevani Andolo
            michaelh Michael Hawkins
            Meirza Meirza
            Ilya Tregubov Ilya Tregubov
            Ron Carl Alfon Yu Ron Carl Alfon Yu
            Votes:
            1 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved:

                Estimated:
                Original Estimate - 0 minutes
                0m
                Remaining:
                Remaining Estimate - 0 minutes
                0m
                Logged:
                Time Spent - 3 days, 43 minutes
                3d 43m

                  Error rendering 'clockify-timesheets-time-tracking-reports:timer-sidebar'. Please contact your Jira administrators.