How would you prevent malicious login attempts?
I'm curious how the DEV developers(those interested in security) here would prevent malicious login attempts.
My solution.
If a IP(e.g: 127.0.0.1
) had failed to authenticate 3 or more times within a 12 hour period, block any further login attempts for 24 hours. This is global, this IP may not attempt to login to any accounts, not just that one account.
If 3 or more IPs have exceeded the 3 failed attempts on a specific account, all IPs are required to supply the federated account, on that specific account.
During those 24 hours of "blocked" login attempts, they are required to supply the account's federated account, such as an email(e.g: you@mail.com
).
If they supply the correct federated account, email in this case, the server will send a special randomly generated link to you@mail.com
that email that temporarily lets you bypass the blocked login attempts.
That link is valid for a short amount of time, e.g: 5-15 minutes.
You provide your credentials and you're logged in. However if the credentials are invalid twice, set a cool-down time of 60 minutes before another link may be sent.
If the attacker has access to your email, then you have a lot more problems. But this thwarts brute-force attacks directly against your credentials, while still being able to gain access to your account.
Remember, the goal is to prevent malicious third-parties from attempting to gain access to the account, while not blocking access for the actual people.
Top comments (11)
Actually, it's not as simple as you think. I'll try to explain it again. Let me demonstrate a attack.
First, I use the IP
192.168.0.16
for example. I am attacking the account with a username ofjohndoe
. I attempt to gain access tojohndoe
3 times with this IP. Then, this IP is no longer allowed to attempt to login to any account without first a email being sent to the account's email, for 24 hours. This means I can't attempt to login tojohndoe
orjanedoe
for 24 hours with this IP.So ... I use another IP.
192.168.0.32
. I try againstjohndoe
again. 3 times, I failed. Now this IP can't attempt to login anywhere.Now I change my IP
192.168.0.64
. I go again, but I fail yet again another 3 times. I've failed in a total of 9 times. I can no longer use192.168.0.16
,192.168.0.32
or192.168.0.64
to attempt to login to ANY accounts.Now
johndoe
is locked. I try once again to attack the account, with the IP of192.168.0.128
, but now I am prompted to supplyjohndoe
's email. In some cases, no one may know this account's email.Crap. Now I can't try to login at all with those three IPs anymore, and now for 24 hours this account is locked down, meaning no one can try to login without having access to the account's email.
By definition, a IP has 3 attempts to gain access to account. After that, if that IP is in use, they must have access to any account's email and receive a special link that allows them to bypass.
So this is a combination of account and IP based locking. Essentially each IP has 3 attempts in 24 hours. Each account has 9 attempts in 24 hours.
If either of those reach their limits, they become locked.
But remember, it's implemented in a way to completely stop this form of attack, while only causing minor inconveniences for real users.
I guess the question is why do we want to do this. As Jorge mentioned, blocking by IP is problematic and can inadvertently block legitimate users. Like, I can run my malicious requests through something like mitmproxy and programmatically generate a random IP for every single login request. You can do it through Postman. Trivial to bypass, but possibility of hurting real users.
Per-account, instead of IP, lockouts make more sense to me. Keep a counter in the users table of the DB, increment upon failed login attempts. At whatever threshold you want (I suggest 5 max), lock the user account for 12-24 hours. You can decide if that's enough or if you want to track # of lockouts and eventually require reaching out to support to re-enable an account.
If you really want to track IP and block in that way, I would include an IP blacklist table in the DB and track failed login attempts in the Redis/whatever cache. Once over the limit, write IP to DB. DB function should run every so often and clean IPs from the table after a certain time threshold so they're not permanently blocked. As I finish writing that I think there are cleaner ways to track IP in the DB but that was off the top of my head.
I do like your idea of caching the IPs, watching them and then persisting their storage if they continue to be suspicious.
However, remember the mechanism.
The definition of locking out an account or IP, means they must supply a email address to that account instead of a username and password.
If the IP
192.168.0.16
attempt 3 times and fails, then a session associated with that IP must supply a email address for all accounts, AKA locking that IP.Why do this by IP?
Because what if this IP wants to try common passwords against a bunch of accounts? It has three attempts, then the IP is locked.
By doing this, a real user would input their email address, go to their email, and receive the temporary login link. Which means that it's a special, randomly generated link that's not easily guessable. They go there, and then they provide their credentials.
This causes a minor inconvenience for real users, and prevents malicious third-parties from hammering account credentials.
But what about multiple IPs?
We also lock by account.
So by locking by account and IP we benefit from both. And it's done in a way that real users can still login even if their accounts or IPs are locked.
The only effective way to do this type of attack, guessing common passwords against multiple accounts, is in some automated fashion. In which case, anti-CSRF tokens on login via a pre-session prevent that behavior.
Nice, so would having CSRF tokens prevent automated registration and bot spam too?
Also, I don't think CSRF tokens should be the only solution to this issue. What if we're building a RESTful HTTPS API and not a website? Then aren't the CSRF tokens useless?
CSRF will prevent malicious actions like trying to log into someone else's account. A bot could still script it's way into creating an account on your website by taking valid, automated actions. You could do something like Google captcha, but that's such an annoying user experience. Requiring users to confirm account creation via email link is a decent way to stop most bots.
Yes, CSRF is not valid if there is no browser involved, as it abuses default browser behavior. Some out-of-band authentication method, like verifying your account creation via email, can help prevent spamming. It doesn't block those attempts, however. Just presents them from working.
Also, 2FA is the best way to stop bots IMO. Works as an additional layer of defense against someone malicious trying to log into a user's account as well.
If CSRF token could prevent automated logins, wouldn't it also protect against automated registering?