1
0
mirror of https://github.com/Mailu/Mailu.git synced 2025-08-10 22:31:47 +02:00

Only account for distinct attempts in rate limits

This commit is contained in:
Florent Daigniere
2023-04-01 11:33:02 +02:00
parent b243ea084d
commit 04a2cdab2f
8 changed files with 17 additions and 8 deletions

View File

@@ -44,7 +44,7 @@ DEFAULT_CONFIG = {
'AUTH_RATELIMIT_IP': '5/hour',
'AUTH_RATELIMIT_IP_V4_MASK': 24,
'AUTH_RATELIMIT_IP_V6_MASK': 48,
'AUTH_RATELIMIT_USER': '100/day',
'AUTH_RATELIMIT_USER': '50/day',
'AUTH_RATELIMIT_EXEMPTION': '',
'AUTH_RATELIMIT_EXEMPTION_LENGTH': 86400,
'DISABLE_STATISTICS': False,

View File

@@ -85,6 +85,7 @@ def handle_authentication(headers):
raw_user_email = urllib.parse.unquote(headers["Auth-User"])
raw_password = urllib.parse.unquote(headers["Auth-Pass"])
user_email = 'invalid'
password = 'invalid'
try:
user_email = raw_user_email.encode("iso8859-1").decode("utf8")
password = raw_password.encode("iso8859-1").decode("utf8")
@@ -107,6 +108,7 @@ def handle_authentication(headers):
"Auth-Server": server,
"Auth-User": user_email,
"Auth-User-Exists": is_valid_user,
"Auth-Password": password,
"Auth-Port": port
}
status, code = get_status(protocol, "authentication")
@@ -115,6 +117,7 @@ def handle_authentication(headers):
"Auth-Error-Code": code,
"Auth-User": user_email,
"Auth-User-Exists": is_valid_user,
"Auth-Password": password,
"Auth-Wait": 0
}
# Unexpected

View File

@@ -48,7 +48,7 @@ def nginx_authentication():
if headers.get("Auth-Status") == "OK":
utils.limiter.exempt_ip_from_ratelimits(client_ip)
elif is_valid_user:
utils.limiter.rate_limit_user(username, client_ip)
utils.limiter.rate_limit_user(username, client_ip, password=response.headers.get('Auth-Password', None))
elif not is_from_webmail:
utils.limiter.rate_limit_ip(client_ip, username)
return response

View File

@@ -68,9 +68,13 @@ class LimitWraperFactory(object):
app.logger.warn(f'Authentication attempt from {ip} for {username} has been rate-limited.')
return is_rate_limited
def rate_limit_user(self, username, ip, device_cookie=None, device_cookie_name=None):
def rate_limit_user(self, username, ip, device_cookie=None, device_cookie_name=None, password=None):
limiter = self.get_limiter(app.config["AUTH_RATELIMIT_USER"], 'auth-user')
if self.is_subject_to_rate_limits(ip):
truncated_password = hmac.new(bytearray(username, 'utf-8'), bytearray(password, 'utf-8'), 'sha256').hexdigest()[-6:]
if password and self.storage.get(f'dedup2-{username}-{truncated_password}') > 0:
return
self.storage.incr(f'dedup2-{username}-{truncated_password}', limits.parse(app.config['AUTH_RATELIMIT_USER']).GRANULARITY.seconds, True)
limiter.hit(device_cookie if device_cookie_name == username else username)
""" Device cookies as described on:

View File

@@ -59,7 +59,7 @@ def login():
flask.flash(msg, "error")
return response
else:
utils.limiter.rate_limit_user(username, client_ip, device_cookie, device_cookie_username) if models.User.get(username) else utils.limiter.rate_limit_ip(client_ip, username)
utils.limiter.rate_limit_user(username, client_ip, device_cookie, device_cookie_username, form.pw.data) if models.User.get(username) else utils.limiter.rate_limit_ip(client_ip, username)
flask.current_app.logger.warn(f'Login failed for {username} from {client_ip}.')
flask.flash('Wrong e-mail or password', 'error')
return flask.render_template('login.html', form=form, fields=fields)

View File

@@ -47,10 +47,11 @@ accounts for a specific IP subnet as defined in
``AUTH_RATELIMIT_IP_V4_MASK`` (default: /24) and
``AUTH_RATELIMIT_IP_V6_MASK`` (default: /48).
The ``AUTH_RATELIMIT_USER`` (default: 100/day) holds a security setting for fighting
The ``AUTH_RATELIMIT_USER`` (default: 50/day) holds a security setting for fighting
attackers that attempt to guess a user's password (typically using a password
bruteforce attack). The value defines the limit of authentication attempts allowed
for any given account within a specific timeframe.
bruteforce attack). The value defines the limit of distinct authentication attempts
allowed for any given account within a specific timeframe. Multiple attempts for the
same account with the same password only counts for one.
The ``AUTH_RATELIMIT_EXEMPTION_LENGTH`` (default: 86400) is the number of seconds
after a successful login for which a specific IP address is exempted from rate limits.

View File

@@ -47,7 +47,7 @@ Or in plain english: if receivers start to classify your mail as spam, this post
<label>Authentication rate limit per user</label>
<!-- Validates number input only -->
<p><input class="form-control" style="width: 9%; display: inline;" type="number" name="auth_ratelimit_user"
value="100" required > / day
value="50" required > / day
</p>
</div>

View File

@@ -0,0 +1 @@
Change the behaviour of AUTH_RATELIMIT_USER and only account for distinct attempts. Same username and same password is now a only accounted once per period.