summary refs log tree commit diff
path: root/synapse/handlers
diff options
context:
space:
mode:
authorBrendan Abolivier <contact@brendanabolivier.com>2019-03-18 12:57:20 +0000
committerRichard van der Hoff <1389908+richvdh@users.noreply.github.com>2019-03-18 12:57:20 +0000
commit651ad8bc96d360500e7f5953d05ef418b51acc86 (patch)
treeeb02892a8aec9fb521e2fc5496d9e74985a04986 /synapse/handlers
parentAdd ratelimiting on login (#4821) (diff)
downloadsynapse-651ad8bc96d360500e7f5953d05ef418b51acc86.tar.xz
Add ratelimiting on failed login attempts (#4865)
Diffstat (limited to 'synapse/handlers')
-rw-r--r--synapse/handlers/auth.py28
1 files changed, 23 insertions, 5 deletions
diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py
index 74f3790f25..caad9ae2dd 100644
--- a/synapse/handlers/auth.py
+++ b/synapse/handlers/auth.py
@@ -101,6 +101,7 @@ class AuthHandler(BaseHandler):
         self._supported_login_types = login_types
 
         self._account_ratelimiter = Ratelimiter()
+        self._failed_attempts_ratelimiter = Ratelimiter()
 
         self._clock = self.hs.get_clock()
 
@@ -729,9 +730,16 @@ class AuthHandler(BaseHandler):
         if not known_login_type:
             raise SynapseError(400, "Unknown login type %s" % login_type)
 
-        # unknown username or invalid password. We raise a 403 here, but note
-        # that if we're doing user-interactive login, it turns all LoginErrors
-        # into a 401 anyway.
+        # unknown username or invalid password.
+        self._failed_attempts_ratelimiter.ratelimit(
+            qualified_user_id.lower(), time_now_s=self._clock.time(),
+            rate_hz=self.hs.config.rc_login_failed_attempts.per_second,
+            burst_count=self.hs.config.rc_login_failed_attempts.burst_count,
+            update=True,
+        )
+
+        # We raise a 403 here, but note that if we're doing user-interactive
+        # login, it turns all LoginErrors into a 401 anyway.
         raise LoginError(
             403, "Invalid password",
             errcode=Codes.FORBIDDEN
@@ -956,13 +964,23 @@ class AuthHandler(BaseHandler):
     def ratelimit_login_per_account(self, user_id):
         """Checks whether the process must be stopped because of ratelimiting.
 
+        Checks against two ratelimiters: the generic one for login attempts per
+        account and the one specific to failed attempts.
+
         Args:
             user_id (unicode): complete @user:id
 
         Raises:
-            LimitExceededError if the ratelimiter's login requests count for this
-                user is too high too proceed.
+            LimitExceededError if one of the ratelimiters' login requests count
+                for this user is too high too proceed.
         """
+        self._failed_attempts_ratelimiter.ratelimit(
+            user_id.lower(), time_now_s=self._clock.time(),
+            rate_hz=self.hs.config.rc_login_failed_attempts.per_second,
+            burst_count=self.hs.config.rc_login_failed_attempts.burst_count,
+            update=False,
+        )
+
         self._account_ratelimiter.ratelimit(
             user_id.lower(), time_now_s=self._clock.time(),
             rate_hz=self.hs.config.rc_login_account.per_second,