Preventing malicious authentication attempts while avoiding CAPTCHAs.

tarialfaro profile image Tari R. Alfaro ・5 min read

It's imperative that we protect credentials with great care, such as properly hashing the low entropy secrets with algorithms like Argon2 or Scrypt.

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.

Just use CAPTCHAs! Hold your thought on that.

Usually it's still possible to attack a lot of services by trying well known "secrets" against a bunch of accounts. Some services attempt to prevent this with CAPTCHAs(official site).

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.

My solution.

The foreign account.

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.

The data.

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.

However if the data comes from a CSPRNG output with >= 128 bits of entropy, we use an easy to compute hashing function. Take the BLAKE2 hashing algorithm for example.

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):


The locking mechanism.

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.

What do we lock, when and for how long?

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.

  1. 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.

  2. 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.

If you have any questions or you'd like to discuss anything, contact me via email, Mastodon, the forum, DEV.to or XMPP.

Cross posted from here.


Editor guide
zanehannanau profile image

CAPTCHA would be the sort of thing you'd put in an abusable, expensive endpoint (eg session token creation or full text search), or else to prevent automatons creating loads of single-use accounts. You can deal with it on the client or the server, but if it isn't matching, you kill it off ASAP.

It's there to reduce bot activity in things that could be expensive, not to be a bar of entry for a returning user who may have already invested in your product.

tarialfaro profile image
Tari R. Alfaro Author

Discord is an example of using CAPTCHAs during sign in process. So is Riot.

Password hashing functions are expensive operations. Which you must take the password input, hash it, then compare it with the stored hash.

zanehannanau profile image

That falls under session token creation though.

I mean bar of entry as requirement after first failure, usually by IP and ID. It might be expensive, but with a use-after-fail it might make more sense.

I say this knowing that I'm simultaneously recommending two practises that are non-similar... it made a lot more sense in my head, anyway.

The tl;dr is that only put it in if you have some need not to, keep tokens that allow bypassing any other requirements or so on.

Thread Thread
tarialfaro profile image
Tari R. Alfaro Author

Ah, okay. That makes more sense.

tobiassn profile image
Tobias SN

CAPTCHAs aren’t for security, they’re for preventing bots from for example creating a ton of accounts or spamming a contact form.

tarialfaro profile image
Tari R. Alfaro Author

Actually, CAPTCHAs are also used for sign in processes in some services. Discord for example takes this approach. And it provides security ... KIND OF.

If you have a script/bot hammering sign in page, you could protect it with CAPTCHAs. The whole point of CAPTCHAs is to prevent bots/scripts.

tobiassn profile image
Tobias SN

I never said anything about sign in processes.