diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index b84fae8..00e7447 100644
--- a/django/contrib/auth/views.py
+++ b/django/contrib/auth/views.py
@@ -303,6 +303,8 @@ def password_reset_done(request,
     return TemplateResponse(request, template_name, context)
 
 
+SECURE_PASSWORD_RESET = True
+
 # Doesn't need csrf_protect since no-one can guess the URL
 @sensitive_post_parameters()
 @never_cache
@@ -333,6 +335,10 @@ def password_reset_confirm(request, uidb64=None, token=None,
     except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
         user = None
 
+    internal_token = request.session.get('internal-reset-token')
+    if internal_token:
+        token = internal_token
+
     if user is not None and token_generator.check_token(user, token):
         validlink = True
         title = _('Enter new password')
@@ -342,11 +348,19 @@ def password_reset_confirm(request, uidb64=None, token=None,
                 form.save()
                 return HttpResponseRedirect(post_reset_redirect)
         else:
+            if SECURE_PASSWORD_RESET and not request.session.get('internal-reset-token'):
+                import uuid
+                # Saving a dummy password to invalidate user token.
+                user.set_password(str(uuid.uuid4()))
+                user.save()
+                request.session['internal-reset-token'] = token_generator.make_token(user)
             form = set_password_form(user)
     else:
         validlink = False
         form = None
         title = _('Password reset unsuccessful')
+        if request.session.get('internal-reset-token'):
+            del request.session['internal-reset-token']
     context = {
         'form': form,
         'title': title,
