A New Look at Preventing Brute Force Attacks
The usual question: How do I prevent brute force attacks?
The usual answer: Create some sort of lock out process. This lockout process usually involves something like this:
After some number of attempts, prevent the user from logging in unless the account is unlocked using some sort of secondary process.
The secondary process could involve responding to an email on a different account, receiving a token via an out-of-band communication (something like Authy, Google Authenticator, a token mailed to a secondary email account, or the now not-recommended SMS messaging), or having an administrator verify the user through a phone conversation. These lockouts can be permanent or even time delayed.
While this approach does provide protections to the user’s account, it comes with some negatives. The first is that it requires an additional process (either technical implementations or support side) to handle account lockouts. There is an increased business cost to provide technical support for these situations.
The second is the impact to the end user. Users can be locked out without even attempting to log in, creating confusion and frustration. Finally, there is the chance of someone locking out a significant number of users to perform a denial of service attack against an organization.
A Different Approach
My co-worker Shawn Lee and I knew there had to be another way to combat brute force attacks. We did a proof of work concept with a client to test our theory out. The general workflow involves the following steps:
- The server generates a random long hex value and stores the value in the database for validation
- The server then sends the random hex value in a JWT (signed HASH 384 algorithm)
- The server generates the difficulty level of work based on the number of previous failed attempts for a given username or the number of attempts by an IP address. This difficulty level algorithm can be altered based on any number of factors
- The client must reverse the JWT token in order to get the original random hex value
- The client generates a random hex value based on the difficulty level the server sent down and performs some calculation (our implementation is a SHA384 hash of the sum of hex values with a bit shift) in order to generate a zero result. The reason for the SHA384 is in order to avoid any potential hardware acceleration used for the more common SHA256
- The client sends the original hex value, random value, and credentials entered by the user
- The server performs validation by comparing the original hex value, the credentials, and the same math computation that the client performed as well as validating the credentials
- The server records success or failure of the attempt against the original hex value stored in the database in order for the next attempt’s difficulty level to be calculated.
As you might imagine, this method involved some additional complexity compared to normal authentication and lockout workflows, but does it remove the problems with lockout processes and lower the overall risk to the user and organization?
Assessing the Concept
In order for us to consider this a success, a set of conditions must be met:
- The end user cannot be severely negatively impacted by the time it takes the client to complete the work behind the scenes
- A brute force attacker must be negatively impacted, with the brute force attack sufficiently slowed by failed authentication attempts
- A specific token may only be valid for one request, must not be reusable, and should be usable to identify attempts and learn more about when or how a brute force attack is being propagated
- The process should be less complex to manage than the account lockout process
Timing is Key
The key to this working is that there must be a balance between the limited impact to the legitimate end user and a delay long enough to the attacker that makes the attack harder. That means that the maximum level of effort should be as close to the real entry of a username and password combination as possible to limit the negative impact of the real user experience. During the proof of concept, we saw that the entire process took anywhere from 3 ms to 20 ms.
By all accounts, our proof of concept was a success.
Is This the End of Brute Force?
As is always the case with security, a dedicated attacker with sufficient resources will still succeed eventually. However, the implementation of our token tracking system actually identifies all attempts, regardless of whether the username is correct or not. We also track IP addresses and failure reasons within the authentication flow to capture if the token was invalid, the math failed, an invalid IP address was used, username was invalid, password was invalid, and the time expiration for the token.
This information will facilitate putting monitoring and corrective controls in place to assist in tracking down longer, drawn-out attacks or quick floods of attacks. Like all systems, this will not guarantee security, but it helps us learn and deter future attacks. Feeding this information into a tool like Nuix Insight Analytics & Intelligence and using its time graph, for example, to chart and analyze attacks is a great way to build your understanding of how attacks are targeted and carried out.
Simple account lockouts do no such thing to help deal with new or varied attacks. Even with the added complexity up front, why would you look for new approaches to a problem that’s not likely to go away anytime soon?