Files
Grant Cox 02527772bd Fix race condition vulnerability, by ensuring the unconfirmed_email is always saved (#5784)
Fix security issue in the `Confirmable` "change email" flow, where a user can end up confirming an email address that they have no access to.  The flow for this is:

1. Attacker registers `attacker1@email.com`
2. Attacker changes their email to `attacker2@email.com`, but does not yet confirm this
3. Attacker submits two concurrent "change email" requests
  a. one changing to `attacker2@email.com`
  b. one changing to `victim@email.com`

When request 3.a is run, the `Confirmable.postpone_email_change_until_confirmation_and_regenerate_confirmation_token` method sets both the `unconfirmed_email` and `confirmation_token` properties.  But as the `unconfirmed_email` value is the same as the model already had from step 2, this attribute is not included in the SQL `UPDATE` statement.  The SQL `UPDATE` statement only updates the `confirmation_token`.  This token is emailed to the `attacker2@email.com` address.

If the "victim" race request (3.b) completes first, it will update both the `unconfirmed_email` and the `confirmation_token`.  But then request 3.a will replace just the token.  The model's end state is having the confirmation token that was sent to the attacker, but with the `unconfirmed_email` of the victim.

When the attacker follows the confirmation link, they will have confirmed the victim's email address, on an account that the attacker controls.

Co-authored-by: Carlos Antonio da Silva <carlosantoniodasilva@gmail.com>
2026-03-16 17:40:45 -03:00
..
2023-10-11 19:12:53 -03:00