From b66ccdc206f218d99d81349f2340fe2317ec7bf7 Mon Sep 17 00:00:00 2001 From: Vadym Shaveiko Date: Sun, 17 May 2026 01:30:57 -0400 Subject: [PATCH] =?UTF-8?q?Enable=20Devise=20:lockable=20=E2=80=94=20brute?= =?UTF-8?q?-force=20protection=20on=20/users/sign=5Fin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User model already includes :lockable (DB columns are migrated) but the config block in `devise.rb` was entirely commented out, so the strategy defaulted to :none — no lockout, no rate limit, nothing standing between the internet and Devise's bcrypt loop. Self-hosted deployments expose docuseal./users/sign_in publicly (the embedded builder requires an admin session). Lock the account on 10 failed attempts, recover via email OR 1-hour wait. --- config/initializers/devise.rb | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index efa97448..8e7b37bf 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -218,27 +218,17 @@ Devise.setup do |config| # config.timeout_in = 30.minutes # ==> Configuration for :lockable - # Defines which strategy will be used to lock an account. - # :failed_attempts = Locks an account after a number of failed attempts to sign in. - # :none = No lock strategy. You should handle locking by yourself. - # config.lock_strategy = :failed_attempts - - # Defines which key will be used when locking and unlocking an account - # config.unlock_keys = [:email] - - # Defines which strategy will be used to unlock an account. - # :email = Sends an unlock link to the user email - # :time = Re-enables login after a certain amount of time (see :unlock_in below) - # :both = Enables both strategies - # :none = No unlock strategy. You should handle unlocking by yourself. - # config.unlock_strategy = :both - - # Number of authentication tries before locking an account if lock_strategy - # is failed attempts. - # config.maximum_attempts = 20 - - # Time interval to unlock the account if :time is enabled as unlock_strategy. - # config.unlock_in = 1.hour + # The User model declares `:lockable` so the columns exist; without the + # config below the strategy is :none → no brute-force protection on + # /users/sign_in. Self-hosted DocuSeal exposes that endpoint publicly + # at docuseal., so lock the account on 10 wrong attempts and + # require an unlock email OR 1h wait to recover. + config.lock_strategy = :failed_attempts + config.unlock_keys = [:email] + config.unlock_strategy = :both + config.maximum_attempts = 10 + config.unlock_in = 1.hour + config.last_attempt_warning = true # Warn on the last attempt before the account is locked. # config.last_attempt_warning = true