pennersr / django-allauth

Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.
https://allauth.org
MIT License
9.37k stars 3.01k forks source link

Vulnerability: Password reset emails sent to unknown addresses #3333

Closed alexburner closed 1 year ago

alexburner commented 1 year ago

Related issue: https://github.com/pennersr/django-allauth/issues/2360

My team just discovered this vulnerability — currently, a user can enter any email address in the password reset form, and that address will receive an email from our system.

⚠️ This means a bad actor could "spam" a large number of addresses with these emails, and tank our domain reputation ⚠️

I understand that the ResetPasswordForm should submit successfully regardless, with ACCOUNT_PREVENT_ENUMERATION=True, to prevent users from determining who's registered with the system.

But why send actually send an email to these unknown addresses? Couldn't allauth just fail silently, in that case?

I see the _send_unknown_account_mail method got added in the original ACCOUNT_PREVENT_ENUMERATION commit: https://github.com/pennersr/django-allauth/commit/7a719b64154840a8c6ff5c4301568f897721fcc0#diff-aca468f72aa7648b36b8ee86a25c3ade350120bc2769cd875e418b1f50574935R536

Could we get an additional configuration flag, such as SEND_PASSWORD_RESET_TO_UNKNOWN_ADDRESSES=False, to prevent this behavior?

alexburner commented 1 year ago

For posterity, we were able to override this with a custom form:

mysite/forms.py

from allauth.account.forms import ResetPasswordForm

class CustomResetPasswordForm(ResetPasswordForm):
    def save(self, request, **kwargs):
        email = self.cleaned_data["email"]
        if self.users:
            self._send_password_reset_mail(request, email, self.users, **kwargs)
        return email

settings.py

ACCOUNT_FORMS = {'reset_password': 'mysite.forms.CustomResetPasswordForm'}

It's worth noting that the allauth documentation recommends against skipping super() like this:

# Ensure you call the parent class's save.

https://django-allauth.readthedocs.io/en/latest/forms.html#reset-password-allauth-account-forms-resetpasswordform

pennersr commented 1 year ago

The password reset is rate limited, mitigating the ability for a bad actor to spam like this.

The attack vector you are describing is not addressable by changing the password reset. The bad actor could just as well spam the signup page, that will result in sending email verification mails as well. But, that is rate limited as well.

So all in all, the issue is not fixable by focusing solely on the password reset. Built-in rate limiting already mitigates this. If that is not enough, you would need to resort to using a captcha system.

tykling commented 9 months ago

just as a heads up to others trying the above workaround from @alexburner and running into circular import issues, I ended up bypassing the email sending to unknown accounts in another way: https://github.com/bornhack/bornhack-website/commit/3c8e0aedbcf0c6897a25a558abd6b0acb96c8b0f

ps. we already have a captcha of sorts on the signup form but the pw reset form was getting spammed