Description
A Django login flow that only calls authenticate and login, without controls such as failure limits, delays or lockouts, lets an attacker repeatedly try passwords.
Potential impact
- An attacker may guess an account password through brute force.
- Authentication failures may be difficult to detect or block.
Remediation
- Track failed login attempts by account, IP address and device.
- Apply a verified defense layer such as
django-ratelimitordjango-axes.
Examples
These are excerpts from inside a login function. check_login_attempts must be implemented by the application or connected to a verified defense layer; adding the call alone does not create a limit. Record authentication failures and use them when deciding whether to allow subsequent attempts.
Before
python
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
After
python
if not check_login_attempts(request):
return None
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
Explanation:
- Before: Calls
authenticateandloginwithout failure limits, delays, lockouts or CAPTCHA, allowing repeated password guessing. - After: Tracks failed attempts by account, IP address and device, applying a delay or lockout when a threshold is exceeded.