-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Fix email enumeration vulnerabilities in password reset and registration flows #5790
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
Open
natevick
wants to merge
3
commits into
heartcombo:main
Choose a base branch
from
natevick:fix-recoverable-email-enumeration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+141
−33
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <p>Hello <%= @resource.email %>!</p> | ||
|
|
||
| <p>Someone tried to create an account using your email address, but an account already exists.</p> | ||
|
|
||
| <p>If this was you, you can:</p> | ||
| <ul> | ||
| <li><%= link_to "Sign in to your account", new_session_url(@resource, @scope_name) %></li> | ||
| <li><%= link_to "Reset your password", new_password_url(@resource, @scope_name) %> if you've forgotten it</li> | ||
| </ul> | ||
|
|
||
| <p>If this wasn't you, you can safely ignore this email. Your account is secure.</p> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,11 +118,23 @@ def with_reset_password_token(token) | |
|
|
||
| # Attempt to find a user by its email. If a record is found, send new | ||
| # password instructions to it. If user is not found, returns a new user | ||
| # with an email not found error. | ||
| # with no errors to prevent email enumeration attacks. | ||
| # Attributes must contain the user's email | ||
| def send_reset_password_instructions(attributes = {}) | ||
| recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found) | ||
| recoverable.send_reset_password_instructions if recoverable.persisted? | ||
|
|
||
| if recoverable.persisted? | ||
| recoverable.send_reset_password_instructions | ||
| else | ||
| # Perform similar work to mitigate timing attacks | ||
| # Generate a token even though we won't use it | ||
| Devise.friendly_token(20) | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what's missing is to simulate up to 50ms for an UPDATE statement that is done here: |
||
| # Always return a new user with no errors to prevent email enumeration | ||
| recoverable = new | ||
| recoverable.errors.clear | ||
| end | ||
|
|
||
| recoverable | ||
| end | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [tools] | ||
| ruby = "3.1.6" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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 like the idea and i was about to do the same. But to properly simulate, we need to also simulate sending an email
I had the same idea as @gregmolnar in his comment about the sign up strategy. Basically here is the flow:
params[:email]the user sends.MaybeSendResetPasswordInstructions.perform_later(params[:email])then obviously my job delegates to devise's Recoveral
send_reset_password_instructionsAPI.The issue here is that the email sending is done in a synchronous way, so it's either a POST request to an email provider such as Sendgrid, or an SMTP call ( basically a network call )
So here i dont know maybe do
sleep 0.5or something. Or add a config in Recoverable which isconfig.send_emails_in_backgroundor similar. if it's true, then we queue a job, if not we sleep for a fixed amount to simulate a network callThere 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.
Let me know what you think, but this timing issue was reported by a pentester as well, and i was shocked about the difference between 40ms when the user does not exist, and 600ms to 1000ms when it exists.
I directly remembered that Devise NEVER sends their email in a background job ( I believe there is an open issue about that )
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.
probably this could help if it gets merged soon: #5610
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'm open to making changes, my biggest concern is that the work will never get merged.