Problem/Motivation

When increasing the minimum required password strength, this new minimum required password strength level will only be enforced for users who change their password.

Problem #1: Users who don't try to change their password will be able to keep using their old (weak) password to log into the site.

Problem #2: Logged in user with a weak password will remain logged in, and depending on the site configuration, users may never log out, leaving their weak password in place for attackers to break into their account.

Proposed resolution

Track individual user's password strength score when they set their password, and when their password score is lower than the site-wide minimum score, force them to change their password by redirecting them to their account edit form. User's individual score is kept in the users table data field (all operations are done on a per user basis, no bulk emailing or anything). The last policy change timestamp is stored as in Drupal variable.

Authenticated users workflow: to prevent users who may be in the middle of a form submission from losing their work, implement a 2h time-window of leeway during which a warning dsm will tell them know they have to change their password with a link if they want to do it right away. Once 2h after the policy change have past, they will get redirected to the user edit form where they can change their password.

Logged out users workflow: for users who were logged out at the time of the policy change, they will be forced to change their password right after they log in, and will not be able to go anywhere else on the site until they have changed their password.

Remaining tasks

Agree on approach and write a patch.

User interface changes

TBD

API changes

TBD

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

greggles’s picture

I like everything about this except:

User's individual score is kept in the users table data field

Isn't that column going away? I suggest a new database table: uid | password_strength_score.

coltrane’s picture

Thanks for proposing this scor! I'm in favor of it all but for the proposed authenticated users workflow. I do agree with greggles that a new separate table is best to store a user's score and set time.

I agree with problem #2 but I'm not sure the risk of it is worth the complexity costs of implementing the proposed messaging and 2 hour window solution. I anticipate that logic being somewhat complex to write as well as a further support burden for integration with other modules and workflows. How about instead we:

* have a README and documentation section on mitigating this risk, including links to relevant modules
* relevant modules could include options such as forcing all authenticated users to be logged out on new strength, and
* possible separate module that implements the messaging and window feature

scor’s picture

Issue summary: View changes

Isn't that column going away? I suggest a new database table: uid | password_strength_score.

@greggles: Absolutely right! Here is what user_schema() says: Use of this field is discouraged and it will likely disappear in a future version of Drupal.

@coltrane: I'm going to reduce the scope of this issue to only support the "logged out" workflow. I will create separate follow up issues for potential improvements. The only way to deal with logged in users for now will be to force them to change their password by for example forcing a log out via some other means.

scor’s picture

Status: Active » Needs review
FileSize
8.31 KB

Here is my implementation for D7.

  • scor committed 6b23c36 on 2420047-force-pw-change-D6
    backport D7 patch #2420047-4 to D6
    
greggles’s picture

  1. +++ b/password_strength.install
    @@ -37,4 +37,32 @@ function password_strength_requirements($phase) {
    +  $schema['password_strength_score'] = array(
    

    I think there needs to be a hook_update to install this, right? And also to calculate scores for existing users? I'd be fine if the latter item were provided by a drush command instead of a hook_update, since it will potentially be expensive.

  2. +++ b/password_strength.module
    @@ -184,9 +215,17 @@ function password_strength_form_password_submit($form, &$form_state) {
    +  // by user_saved() in user_register_submit(). This ensures that the password
    

    comment typo "user_save()" ?

scor’s picture

Status: Needs review » Needs work

I think there needs to be a hook_update to install this, right?

Good point, I will add that. Good catch on user_saved() too!

And also to calculate scores for existing users?

Nope, we can't do that because we need the raw password to calculate the strength. All users will be assumed to have password too weak and will be asked to reset. From then onwards, we will keep track of their strength. We could potentially add an checkbox in the admin interface to toggle this password change on login. It could be enabled on new install, but an update function could disable it until the admin has warned users they will need to reset their password?

greggles’s picture

Right...that makes sense about not doing it for existing passwords.

scor’s picture

And also to calculate scores for existing users?

Take #2: Actually, there is a solution for that! #2424253: Capture password strength during the login process. That means we could enable this password reset policy via an update function from the get-go once we have #2424253 committed...

scor’s picture

It occurs to me that with #2424253: Capture password strength during the login process we would not really need to keep track of each user's password strength in the DB to solve the "force password change at login" use case. That said having the strength in the DB could open up other applications (for example if we wanted to react to a strength requirement increase immediately).

  • scor committed 5921852 on 2420047-force-pw-change-D6
    fix typo caught by greggles in #2420047-6
    
scor’s picture

Status: Needs work » Needs review
FileSize
11.99 KB
6.75 KB

Folding the idea of #2424253: Capture password strength during the login process here since the code change is actually quite small. I've also fixed @greggles's issues of #6. Once you install this patch / upgrade your module, you will now be able to log in with your strong password without being asked to change it if its strength is as good as the minimum strength, and the strength will be tracked in the DB going forward.

greggles’s picture

Thanks for the improvements, scor. I did a quick visual review and it looks pretty thorough :)

scor’s picture

FileSize
11.73 KB
3.01 KB

Thanks @greggles, it's encouraging.

This patch only include minor code style improvements.

coltrane’s picture

Status: Needs review » Needs work

Tests pass and this looks really good. I just have a couple suggestions.

  1. Will you please add a column for storing the date of entry in password_strength_schema()? Could be useful for quick auditing when the password was set.
  2. Would you please add a drupal_alter() in password_strength_init() on paths? I know some sites have modified some of the user* paths for password changes.
  3. +++ b/password_strength.module
    @@ -598,3 +649,75 @@ function password_strength_required_score($account) {
    +    // steps, so we have to invoked hook_password_strength_change() here.
    

    Minor grammer mistake: "invoke"

Edit: dreditor didn't work right, manually modified the code sections.

banviktor’s picture

Status: Needs work » Needs review
FileSize
12.77 KB
3.24 KB

As @scor is busy, I've extended his patch with the requested features, so this issue won't be blocking the next release. All credits go to @scor !

Also user/logout seems to be the new default logout path, so added that to the list of allowed paths.

coltrane’s picture

Status: Needs review » Needs work

Want to review further but quick nitpick for coding standards:

  1. +++ b/README.md
    @@ -34,4 +34,8 @@ instructions, for example using drush:
    \ No newline at end of file
    

    Bring back newline please, coding standard has ending newline https://www.drupal.org/coding-standards#indenting

  2. +++ b/password_strength.install
    @@ -37,4 +37,44 @@ function password_strength_requirements($phase) {
    \ No newline at end of file
    

    Same regarding newline

banviktor’s picture

Status: Needs work » Needs review
FileSize
12.74 KB
859 bytes

Git diff should handle that notification better. The second occurence is actually not valid, that file missed the line feed before the patch. It could have added a "-" to that line.

README.md however did need a newline.

Edit: Also changed time() to REQUEST_TIME :)

greggles’s picture

FileSize
12.13 KB

Here is a manual reroll as #18 no longer applied. I'm not always good at doing that, so could someone confirm it looks good? :)