Handle race conditions for multiple workers

The creation of several entries is possible due to race conditions between different worker processes, which can later break the usage of get in this situation.
This change should help to prevent this race condition using the database.
This commit is contained in:
Uli Klank 2021-06-22 14:24:38 +02:00 committed by Aleksi Häkli
parent 84b5ccd5d3
commit 93341a4d04

View file

@ -119,9 +119,9 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler):
# 2. database query: Calculate the current maximum failure number from the existing attempts
failures_since_start = 1 + self.get_failures(request, credentials)
# 3. database query: Insert or update access records with the new failure data
try:
attempt = AccessAttempt.objects.get(
if not (settings.AXES_ONLY_USER_FAILURES and username is None):
# 3. database query: Insert or update access records with the new failure data
attempt, created = AccessAttempt.objects.get_or_create(
username=username,
ip_address=request.axes_ip_address,
user_agent=request.axes_user_agent,
@ -130,13 +130,6 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler):
# because attackers can request the site with multiple different configurations
# in order to bypass the defense mechanisms that are used by the site.
log.warning(
"AXES: Repeated login failure by %s. Count = %d of %d. Updating existing record in the database.",
client_str,
attempt.failures_since_start,
get_failure_limit(request, credentials),
)
separator = "\n---------\n"
attempt.get_data = Concat("get_data", Value(separator + get_data))
@ -146,31 +139,26 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler):
attempt.failures_since_start += 1
attempt.attempt_time = request.axes_attempt_time
attempt.save()
except AccessAttempt.DoesNotExist:
# Record failed attempt with all the relevant information.
# Filtering based on username, IP address and user agent handled elsewhere,
# and this handler just records the available information for further use.
if not (settings.AXES_ONLY_USER_FAILURES and username is None):
if created:
log.warning(
"AXES: New login failure by %s. Creating new record in the database.",
client_str,
)
AccessAttempt.objects.create(
username=username,
ip_address=request.axes_ip_address,
user_agent=request.axes_user_agent,
get_data=get_data,
post_data=post_data,
http_accept=request.axes_http_accept,
path_info=request.axes_path_info,
failures_since_start=1,
attempt_time=request.axes_attempt_time,
)
else:
log.warning(
"AXES: Username is None and AXES_ONLY_USER_FAILURES is enable, New record won't be created."
"AXES: Repeated login failure by %s. Count = %d of %d. Updating existing record in the database.",
client_str,
attempt.failures_since_start,
get_failure_limit(request, credentials),
)
else:
log.warning(
"AXES: Username is None and AXES_ONLY_USER_FAILURES is enable, New record won't be created."
)
if (
settings.AXES_LOCK_OUT_AT_FAILURE
and failures_since_start >= get_failure_limit(request, credentials)