-
-
Notifications
You must be signed in to change notification settings - Fork 210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Password check #988
Password check #988
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good start, but more work is required.
- You put an error message in
out['msg']
, that's incorrect since it'll be displayed to the user as a success notification. Instead we should show a confirmation warning to the user withresponse.render('templates/confirm.spt', ...)
. - Your 4th commit sends the full hash to the pwnedpasswords API, we want to use the
/range
endpoint instead. - You only implemented checking the password when it's being modified, but we also want to check during log-in. We'll need to store the time of the last check in our DB so we don't query the API too often.
@Changaco, I don't understand why we need to check the strength of the password during log-in. The password has already been assessed while setting/changing it. |
We need to check during log-in because:
|
www/%username/settings/edit.spt
Outdated
msg = _( | ||
"Are you sure you want to change your password?" | ||
) | ||
raise response.render('templates/confirm.spt', state, cls='warning', msg=msg, back_to=back_to) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This confirmation warning is unnecessary, we should only warn the user when the password is weak or compromised.
www/%username/settings/edit.spt
Outdated
"The new password is not strong enough. We recommend you to choose a combination of letters, symbols, and numbers." | ||
"Are you sure you want to change your password?" | ||
) | ||
raise response.render('templates/confirm.spt', state, msg=msg, back_to=back_to) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need at least two different warning messages here, one when the password is weak, and another when the password is compromised (i.e. when it was found in the pwned database).
www/%username/settings/edit.spt
Outdated
p.update_password(body['new-password']) | ||
out['msg'] = _("Your password has been changed.") | ||
out['msg'] = _("Your password has been changed") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change should be reverted.
To keep track of password checks I suggest using the |
The mentioned changes have been made. Where should I implement the login-check? I am currently doing it in |
@Changaco,
Also, how do I notify the user about his weak password? I tried using |
@umang-malik Dropping To notify the user I see two possibilities:
The latter should be simpler to implement, and it would allow moving the password check to a background worker, so I think it's the best option. |
@Changaco, All the requested things have been done. 😃 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work @umang-malik, it just needs some polishing.
.gitignore
Outdated
@@ -12,7 +12,9 @@ node_modules/ | |||
.tox/ | |||
htmlcov/ | |||
tests/py/fixtures/TestTranslations.yml | |||
tests/py/fixtures/TestLogIn.yml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fixture file should be committed, not ignored.
.gitignore
Outdated
.idea/ | ||
payday-1.txt* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be payday-*.txt
, and if we're adding that then we should also add payday.pid
. Alternatively we could modify the default LOG_DIR
value to put all this stuff in a sub-directory, and then ignore it.
emails/password_warning.spt
Outdated
{{ _("Update your Liberapay password") }} | ||
|
||
[---] text/html | ||
<p>{{ _("{0}",message) }}</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't right, _("{0}", message)
is a no-op. You shouldn't send an already-translated message to a notification template. Instead you should send the variables that the template needs to generate the message. In this case it could be either password_strength
and password_leak_count
, or a password_status
string (possible values: 'weak'
, 'common'
, 'compromised'
). The latter is probably better because it allows changing the thresholds without breaking old notifications.
liberapay/security/authentication.py
Outdated
@@ -14,6 +17,9 @@ | |||
from liberapay.models.participant import Participant | |||
from liberapay.utils import get_ip_net | |||
|
|||
import passwordmeter | |||
from hashlib import sha1 | |||
import requests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't break the import style like this. Imports are split into specific groups (based on PEP8), and ordered alphabetically within each group.
liberapay/security/authentication.py
Outdated
"You are recommended to change your password." | ||
) | ||
p.notify('password_warning', email=False, message=password_check_message) | ||
p.add_event(website.db, 'password-check', None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be moved to a separate function, and the call to that new function should be wrapped to report and mask errors, like this:
try:
check_password(password)
except Exception as e:
website.tell_sentry(e, state)
liberapay/security/authentication.py
Outdated
@@ -57,6 +63,41 @@ def sign_in_with_form_data(body, state): | |||
) | |||
if not p: | |||
state['log-in.error'] = _("Bad username or password.") | |||
else: | |||
last_password_check = p.get_last_event_of_type('password-check') | |||
if (not last_password_check) or utcnow() - last_password_check.ts > timedelta(days=30): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to check every 30 days, once or twice per year should be enough, e.g. days=180
.
www/%username/settings/edit.spt
Outdated
suffix = line.split(":")[0] | ||
if passhash_short + suffix == passhash: | ||
count = int(line.split(":")[1].strip()) | ||
strength, improvements = passwordmeter.test(body['new-password']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is code duplication, it should be replaced with a call to a separate check_password
function.
requirements_base.txt
Outdated
--hash=sha256:a77328ac55dbb5735da99441870251befe135f687ab707a7a178561363b27704 | ||
globre==0.1.5 \ | ||
--hash=sha256:ee214204f237e9114b8f61eeb61c2abd1e665ca3b59e5a6a0b070971c0bb12e2 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've looked through these new dependencies, they appear to be okay (although their source code could use a good cleanup).
One issue is that passwordmeter
includes its own list of ten thousand common passwords, and always loads it, even if we skip the notword
factor (which we should do since it's redundant with the pwnedpasswords check). This wastes approximately 1.2MB of RAM (measured with this get_size
function on my laptop).
@Changaco , all the changes have been made.
|
I've made lots of changes. @umang-malik You should look at them and let me know what you think. Screenshots: |
I dropped the password strength test because it wasn't good. See this blog post for a complete explanation. I didn't replace |
@Changaco, I agree with the given blog's argument. Though I think that keeping a measure of password strength would help in setting a minimum threshold for passwords, especially in languages other than English, as the pwnedpassword database is heavily dominated by the it (and other common languages). For example, |
Thanks for improving the templates. They look a lot better than mine. |
The truth is that for Liberapay anything that's not a long string of random characters is a bad password. It doesn't really matter whether it's |
Well, I guess it shall be fine. We can always pull up a new issue in case we feel the need to do so. |
Signed-off-by: Umang Malik <umang99m@gmail.com>
1. Check if new password exists in pwned password database. 2. Check the strength of a password and ask for a stronger one if password is weak. Signed-off-by: Umang Malik <umang99m@gmail.com>
Changed the new-password to a stronger one for the test to check if changing password works correctly. Signed-off-by: Umang Malik <umang99m@gmail.com>
Signed-off-by: Umang Malik <umang99m@gmail.com>
…n while changing password. 1. Shifted from the to '/range' api for better security. 2. Ask user for confirmation while changing password. 3. Make change to test_sign_in.py in accordance with the above. Signed-off-by: Umang Malik <umang99m@gmail.com>
Different warnings are shown depending if password is weak, commonly used or is compromised. Warning is not shown if password is strong and not compromised. Signed-off-by: Umang Malik <umang99m@gmail.com>
Signed-off-by: Umang Malik <umang99m@gmail.com>
Also fixed add_event for password-check. Signed-off-by: Umang Malik <umang99m@gmail.com>
tests/py/fixtures/TestLogIn.yml, payday-1.txt and payday-1.txt.part are generated while running tox. Signed-off-by: Umang Malik <umang99m@gmail.com>
Signed-off-by: Umang Malik <umang99m@gmail.com>
Also changed payday-1.txt* to payday-*.txt* and added payday.pid to .gitignore. Signed-off-by: Umang Malik <umang99m@gmail.com>
It checks the word against top 10 thousand passwords, which is basically redundant with the pwnedpasswords check. Signed-off-by: Umang Malik <umang99m@gmail.com>
It gives a very high score to 'qwER43@!' and a very low score to 'correcthorsebatterystaple'.
Rebased on master and cleaned up the commit messages. |
@umang-malik In the future you can drop the |
Deployed and announced. |
I've emailed Troy Hunt to let him know about this. |
Whenever the user sets/changes his password, his password is checked in the "pwned passwords" database (https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/). The strength of the password based on the characters is also checked.
If the password is found weak on the basis of the above, then the user is suggested to use a better password.
This PR is with reference to #986.