Locking Users Out After Invalid Login Attempts: Django & a Cache
What & Why
We’ve all done it. Forgotten our passwords and been locked out of an account. It’s irritating when it’s actually you, but it does serve a purpose — namely it will significantly slow down any malicious actors trying to use brute force attacks to access accounts on your system. So let’s jump in!
In order to do this we need to keep track of a few things. We’ll talk about the specifics of how we can store this information in the cache later, but for now, let’s assume we’re tracking this information:
- The user’s invalid login attempts. We don’t need to keep track of all of them indefinitely, we only need to keep track of the ones that were made within the time range, up to the number of invalid attempts they’re allowed to make. To be more specific, if we lock a user out after 5 invalid login attempts within a 10 minute time period, we don’t need to keep track of more than 5 attempts, and we don’t need to keep track of invalid attempts that were more than 10 minutes ago.
- When the user’s lockout started, if they’ve been locked out.
Pseudocode & the Logic Flow
Here are the things that could happen when a user hits the login view with a POST request, and what we should do (a fun flow chart + some words for folks who aren’t visual learners!):
- If they’ve been locked out, make sure the timestamp of the lockout start is within the duration of the lockout.
- If it’s not, delete their entry from the cache, they can start fresh
- If their lockout timestamp is within the lockout duration time period, we don’t need to process their request at all — even if the credentials are correct, we’re not going to log them in, because they’re still locked out
If they’re not locked out:
- Process the request — if their credentials are correct, log them in.
- If they’re incorrect, check the cache to see if they’ve already got an entry. If they don’t have an entry, we’ll add one with the timestamp for
nowto their timestamp bucket
If they’ve entered invalid credentials and already have an entry in the cache: