Ensuring Secure Password Reset in Rails 7.1 with Devise - Any best practices?
I'm following best practices but After trying multiple solutions online, I still can't figure this out. I've been banging my head against this for hours... I've searched everywhere and can't find a clear answer. While conducting a code review for our Rails 7.1 application using Devise for authentication, I noticed some potential vulnerabilities in the password reset functionality. We want to ensure that the password reset process is robust and secure, particularly against attacks like session fixation or brute force attempts. In our implementation, weโre using Devise's built-in methods to handle password resets, but Iโm curious about additional security measures we might be overlooking. Hereโs a snippet of the relevant code: ```ruby class Users::PasswordsController < Devise::PasswordsController def create self.resource = resource_class.send_reset_password_instructions(resource_params) yield resource if block_given? if successfully_sent?(resource) respond_with resource, location: after_sending_reset_password_instructions_path_for(resource) else respond_with resource end end end ``` Weโve set up rate limiting using Rack::Attack to prevent rapid requests for password resets, but I feel like there should be more. Should we also consider using additional measures, like temporary invalidation of tokens after a certain number of attempts or implementing CAPTCHA verification? Another thing Iโm pondering is whether we should log password reset attempts or even send notifications to users when their email is requested for a reset, to add an extra layer of security. What recommendations would you have for enhancing the security of the password reset process, without compromising user experience? Has anyone had experience with similar implementations? Any insights on leveraging Devise efficiently while maintaining security best practices would be appreciated. This is part of a larger API I'm building. This issue appeared after updating to Ruby stable. Is there a simpler solution I'm overlooking?