Multiple factors of authentication is important to protecting accounts. Such as having knowledge of a secret and in possession of something.
Despite properly implementing cryptography and management of credentials, there are still issues such as malicious third-parties attempting to gain access to accounts. And it's hard stopping that all together.
One thing I've noticed is that CAPTCHAs are either accessible or sufficiently resistant to automated actions. Often it's not both. Sometimes it's neither! Even though I don't have any disabilities, I still have a hard time completing them.
Google's reCAPTCHA a lot of the time doesn't like Tor network activity often rejecting valid CAPTCHA answers due to "possible automated queries". Usually the solution I found is to switch up the exit node. It's an issue with popular proxies and VPNs, not just Tor users. More information here.
Unfortunately I don't know of any decent free open-source privacy respecting alternatives to Google's CAPTCHAs.
So CAPTCHAs may not be the best choice.
For each set of credentials that is stored, there is also a unique foreign account associated with it, this could be a email, XMPP or phone number that the authentic individual is in possession of.
There has to be some sort of data sent to the foreign account to prove they have access to it. The data MUST be ephemeral and random. This could be a software token attached to a link where a few authentication attempts are permitted. Or a secret(e.g: 8 digit code) that can easily be typed in.
To securely store the data, and verify the foreign account access data, it must be hashed properly. Generally the secret is low entropy, so we should store it with Argon2 or Scrypt.
For defense in depth or as an alternative strategy: Use asymmetric cryptography to ensure only the authentic individual may access the data. This could be sent to the associated foreign account or given right away.
Example secret(easy to type in):
8 1 7 1 5 9 4 6
Example link(far less guessable):
Now that there are two very important pieces to the puzzle gathered, that being the foreign account and the data. We have one final piece to gather.
What are we going to do with those two pieces? They can be used as a form of two-factor authentication.
By definition "locking" means to apply that 2FA. Of course we could constantly apply it(preventing nearly all malicious authentication attempts), however ... we can try to make educated guesses to only apply the 2FA when it's possible there is an attack on the account.
Which means it's possible to make the accounts a lot more secure while retaining usability.
We can lock an IP that attempted to authenticate and failed a certain number of times within a certain time period. If we lock an IP, it's global. Meaning that IP is not allowed to attempt to authenticate on any accounts without verifying foreign account access. This goes for whitelisted IPs as well.
There is one other way to handle IPs. Each account could have whitelisted IPs that avoid the lock(only if the whitelisted IPs aren't locked), while any unrecognized IPs are automatically required to go through lock, even if the IPs themselves aren't locked. This could provide some better security while still retaining some usability.
Another method is locking by account. If a certain number of authentication attempts fail within a certain time period, it'll be locked. Meaning no IPs may attempt to authenticate on this account. That includes whitelisted IPs.
The only effective way of defending against attacks is to use both methods. Locking by IP and account, preferably with the same allowed authentication fail attempts before locking.
We can't completely rely on the first method because most experienced attackers know that they can just switch up their IP with a proxy.
We also can't completely rely on the second method because one IP could just attack all of the accounts. Instead we use both methods to prevent both issues.
However it's better to only have the second method than to only have the first. And it's best to have both.
If an account is allowed 3 failed attempts within 24 hours, and an IP is allowed 6 failed attempts within 24 hours ... they can use one IP to attack two accounts.
If an account is allowed 6 invalid authentication attempts within 24 hours, and an IP is allowed 3 failed attempts within 24 hours, we can use two IPs to attack one account. This can be worse than 1) because more attempts are available to each account.
For how long the account or IP is locked could fixed, incremental(depending on how many times it was previously locked), or randomized.
There are a few other things to take into consideration. You could make accurate educated guesses by determining how many requests there are to authenticate with which IP, and which account. Monitor and automatically lock all accounts for a certain period of time if you're 99% sure there is a possible attack on a bunch of accounts and warn the users that there is probably an attack that was prevented.
It's one area I'm still exploring, how to accurately determine whether there are attacks ongoing. (Although from my understanding it's extremely hard, it would require a lot of math, possibly even an AI.)
This was my alternative for authentication forms. This isn't meant to replace CAPTCHAs, it's meant to avoid them as much as possible and provide a more secure and accessible solution when authenticating.
The most secure solution is to always apply the 2FA, use asymmetric cryptography and a high entropy piece of data, such as the software token attached to a link. But this is too inconvenient for average users.
Take note that if you're not already logged into your email assuming that's what you use for the foreign account, then the email service could require CAPTCHAs during the sign in process.
This series of posts document a high-level process to use when planning a modern web application, from project organization, collaboration considerations and tooling choices during development, all the way through deployment and performance strategies.