-
Notifications
You must be signed in to change notification settings - Fork 984
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
Disable users with breached passwords #4541
Disable users with breached passwords #4541
Conversation
One additional thing, we probably want to announce this change to pypi-announce and to distutils-sig whenever we merge it. We probably want to get some messaging sorted out for that as well. |
2b33ae2
to
ff596b5
Compare
Ok, besides the two open questions:
this is ready to go. This does add a setting:
However the default for that option is the HIBP service, so we do not need to change anything in production. The local development environment has that setting to enable the |
Thanks. Will take a look tomorrow. |
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.
Some language suggestions based on https://web.archive.org/web/20180410101124/https://material.io/guidelines/style/writing.html and https://plainlanguage.gov/guidelines/words/use-simple-words-phrases/
|
||
# How do you know this? | ||
|
||
We utilize a free security service from HaveIBeenPwned. When registering, |
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.
Use plain English "use" instead of "utilize".
|
||
We utilize a free security service from HaveIBeenPwned. When registering, | ||
authenticating, or updating your password, we generate a SHA1 hash of your password and | ||
use the first five (5) characters of the hash to determine if the password has been |
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.
Just "5" instead of "five (5)".
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.
"determine" - > "decide"
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.
"has been compromised" - > "is compromised"
{% block content %} | ||
# What? | ||
|
||
During your recent attempt to log in or upload to PyPI, we noticed that your 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.
Can delete "that".
# What? | ||
|
||
During your recent attempt to log in or upload to PyPI, we noticed that your password | ||
has appeared in public data breaches. To protect you and other users, we have |
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.
"has appeared in" - > "appears in"
|
||
During your recent attempt to log in or upload to PyPI, we noticed that your password | ||
has appeared in public data breaches. To protect you and other users, we have | ||
preemptively reset your password and you will no longer be able to log in or upload 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.
"you will no longer be able to" - > "you can no longer" or "now you can not"
warehouse/templates/pages/help.html
Outdated
@@ -178,6 +178,9 @@ <h3 id="compromised-password">{{ compromised_password() }}</h3> | |||
<p> | |||
If you receive an error message saying that "This password has appeared in a breach or has otherwise been compromised and cannot be used", you should change it all other places that you use it as soon as possible. | |||
</p> | |||
<p> | |||
If you've received this error while attempting to log in to or upload to PyPI, then your password has been reset and you will no longer be able to log into PyPI until you <a href="{{ request.route_url('accounts.reset-password') }}">reset your password</a>. |
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.
"you've" - > "you",
"log in to" - > "log in",
"you will no longer be able to" - > "you cannot"
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 didn't like the way this sentence read changing you've to you, but I expanded it out to "you have".
tests/unit/accounts/test_services.py
Outdated
( | ||
None, | ||
( | ||
"This password has appeared in a breach or has otherwise " |
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.
"has appeared in" - > "appears in"
Delete "otherwise" and trailing fullstop
tests/unit/accounts/test_services.py
Outdated
( | ||
"http://localhost/help/#compromised-password", | ||
( | ||
"This password has appeared in a breach or has otherwise " |
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.
"has appeared in" - > "appears in"
Delete "otherwise"
warehouse/accounts/services.py
Outdated
@@ -282,6 +286,12 @@ def __eq__(self, other): | |||
|
|||
@implementer(IPasswordBreachedService) | |||
class HaveIBeenPwnedPasswordBreachedService: | |||
|
|||
_failure_message_preamble = ( | |||
"This password has appeared in a breach or has otherwise been compromised and " |
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.
"has appeared in" - > "appears in"
Delete "otherwise" and trailing fullstop
warehouse/accounts/services.py
Outdated
@implementer(IPasswordBreachedService) | ||
class NullPasswordBreachedService: | ||
failure_message = "This password has appeared in a breach." | ||
failure_message_plain = "This password has appeared in a breach." |
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.
"has appeared in" - > "appears in"
Addressed the feedback by @hugovk. |
tests/unit/accounts/test_services.py
Outdated
user_service.update_user(user.id, password="foo") | ||
assert user.password != "!" | ||
|
||
# Now we'll actually test our disble function. |
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.
Should be "disable"
def create_service(cls, context, request): | ||
return cls() | ||
|
||
def check_password(self, password, *, tags=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.
I think I don't quite understand under what circumstances we want to use this?
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 use it in local development, because we set all users to have the password "password", which appears in breaches. Thus without the NullPasswordBreachedService
in local development, we would never be able to log into those users, and would have to generate secure password for them.
warehouse/templates/pages/help.html
Outdated
@@ -178,6 +178,9 @@ <h3 id="compromised-password">{{ compromised_password() }}</h3> | |||
<p> | |||
If you receive an error message saying that "This password has appeared in a breach or has otherwise been compromised and cannot be used", you should change it all other places that you use it as soon as possible. |
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.
Do we need to update the wording of the quoted error message here?
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.
Yup, good catch.
f3ea5d8
to
b4f937c
Compare
Feedback from @brainwane addressed and responded to! |
<h3>What should I do?</h3> | ||
<p> | ||
To regain access to your account, | ||
<a href="{{ request.route_url('accounts.reset-password') }}">reset your password</a> |
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 think this should be request.route_url('accounts.request-password-reset')
# What should I do? | ||
|
||
To regain access to your account, reset your password on PyPI | ||
({{ request.route_url('accounts.reset-password') }}). We also recommend that you go 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.
I think this should be request.route_url('accounts.request-password-reset')
warehouse/templates/pages/help.html
Outdated
If you receive an error message saying that "This password appears in a breach or has been compromised and cannot be used", you should change it all other places that you use it as soon as possible. | ||
</p> | ||
<p> | ||
If you have received this error while attempting to log in or upload to PyPI, then your password has been reset and you cannot log in to PyPI until you <a href="{{ request.route_url('accounts.reset-password') }}">reset your password</a>. |
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 think this should be request.route_url('accounts.request-password-reset')
Noticed that we're sending folks to the wrong URL in the emails/FAQ. This route flashes "Invalid token: no token supplied", as indeed no token is supplied. |
b4f937c
to
fb02683
Compare
Going to go ahead and merge this once tests pass. I believe that all of the feedback was addressed. If there are any further changes we can address that in a future PR. |
This pull request effectively disables a user if their password appears in the HIBP data until they reset their password. Whenever the user attempts to authenticate, we will check their password using the HIBP breached password service, and if it comes back as a positive hit we will disable their password, preventing any future attempts to authenticate, send them an email with information, and finally return an error ultimately preventing them from authenticating.
While we can't iterate over our database and look for any users that have compromised credentials, this will effectively do the same thing since once a password has appeared in the HIBP data, it will no longer be usable to log into PyPI.
This will be in effect both for users authenticating using the log in form in the UI and for users uploading files to PyPI.
This is a fairly abrupt interruption to the user's flow-- they are attempting to do something and we forcibly prevent them and require them to do something else first. However this is considered to be an acceptable trade off in this case, because these password are known to the general public, and can be used in credential stuffing attacks on PyPI. This was recently the cause of a fairly large security issue on NPM, where reused credentials by one of the users for a popular package were used to upload malicious packages (more information).
This branch is currently working, however it still needs:
password
, which doesn't work because it has been compromised. Likely we should just have a no-op breached password service in local development.Maybe give a different error when we're failing during authentication versus when changing the password? Consistency in error message makes it easier to google the problem, but the error message is more generic and harder to know exactly what just happened.I'm submitting this PR now, so that people can start to review the messaging that we're sending to users. Particularly the emails that i've written (plain text and HTML) and the additional information I've added to the FAQ.
Fixes #4471.