Mercurial > kallithea
changeset 8093:8b47181750a8 stable
login: fix incorrect CSRF rejection of "Reset Your Password" form (Issue #350)
htmlfill would remove the CSRF token from the form when substituting the query
parameters, causing password reset to break.
By default, htmlfill will clear all input fields that doesn't have a new
"default" value provided. It could be fixed by setting force_defaults to False
- see http://www.formencode.org/en/1.2-branch/modules/htmlfill.html . It could
also be fixed by providing the CSRF token in the defaults to be substituted in
the form.
Instead, refactor password_reset_confirmation to have more explicitly safe
handling of query parameters. Replace htmlfill with the usual template
variables.
The URLs are generated in kallithea/model/user.py send_reset_password_email()
and should only contain email, timestamp (integer as digit string) and a hex
token from get_reset_password_token() .
author | Mads Kiilerich <mads@kiilerich.com> |
---|---|
date | Thu, 09 Jan 2020 12:28:33 +0100 |
parents | bbbfee57fb96 |
children | 4e0442f914b9 |
files | kallithea/controllers/login.py kallithea/templates/password_reset_confirmation.html kallithea/tests/functional/test_login.py |
diffstat | 3 files changed, 8 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/kallithea/controllers/login.py Thu Jan 09 21:54:27 2020 +0100 +++ b/kallithea/controllers/login.py Thu Jan 09 12:28:33 2020 +0100 @@ -210,12 +210,10 @@ # The template needs the email address outside of the form. c.email = request.params.get('email') - + c.timestamp = request.params.get('timestamp') or '' + c.token = request.params.get('token') or '' if not request.POST: - return htmlfill.render( - render('/password_reset_confirmation.html'), - defaults=dict(request.params), - encoding='UTF-8') + return render('/password_reset_confirmation.html') form = PasswordResetConfirmationForm()() try:
--- a/kallithea/templates/password_reset_confirmation.html Thu Jan 09 21:54:27 2020 +0100 +++ b/kallithea/templates/password_reset_confirmation.html Thu Jan 09 12:28:33 2020 +0100 @@ -22,13 +22,13 @@ ${h.form(h.url('reset_password_confirmation'), method='post')} <p>${_('You are about to set a new password for the email address %s.') % c.email}</p> <p>${_('Note that you must use the same browser session for this as the one used to request the password reset.')}</p> - ${h.hidden('email')} - ${h.hidden('timestamp')} + ${h.hidden('email', value=c.email)} + ${h.hidden('timestamp', value=c.timestamp)} <div class="form"> <div class="form-group"> <label class="control-label" for="token">${_('Code you received in the email')}:</label> <div> - ${h.text('token', class_='form-control')} + ${h.text('token', value=c.token, class_='form-control')} </div> </div>
--- a/kallithea/tests/functional/test_login.py Thu Jan 09 21:54:27 2020 +0100 +++ b/kallithea/tests/functional/test_login.py Thu Jan 09 12:28:33 2020 +0100 @@ -459,12 +459,12 @@ assert response.status == '200 OK' response.mustcontain("You are about to set a new password for the email address %s" % email) response.mustcontain('<form action="%s" method="post">' % url(controller='login', action='password_reset_confirmation')) - response.mustcontain(no='value="%s"' % self.session_csrf_secret_token()) # BUG making actual password_reset_confirmation POST fail + response.mustcontain('value="%s"' % self.session_csrf_secret_token()) response.mustcontain('value="%s"' % token) response.mustcontain('value="%s"' % timestamp) response.mustcontain('value="username@example.com"') - # fake a submit of that form (*with* csrf token) + # fake a submit of that form response = self.app.post(url(controller='login', action='password_reset_confirmation'), {'email': email,