How to Overcome Credential Stuffing Attacks

Kuba Zarzycki
Daftcode Blog
Published in
9 min readFeb 27, 2019

--

Illustration by Justyna Mieleszko

OOver the span of last months, we can observe a rising trend in web security threats — massive attacks targeting users who reuse their passwords across multiple online services. Attackers acquire large collections of previously breached usernames and passwords and then try to log in to various websites, expecting that they’ll find some percentage of accounts with matching credentials.

Their assumption is totally correct because estimations say that as many as 60% of users use only one password for all of their accounts. The practice of brute forcing login pages with leaked data is called credential stuffing and it’s a real problem for many online services such as hugely popular game Fortnite, HSBC bank or more recently productivity tool — Basecamp. Motivations of attackers depend on the type of web app, sometimes it can be access to confidential data, stealing deposited funds or spreading scam messages in social media. Although it might seem like it’s only users’ problem, it can also hurt your business in an unexpected way, like in case of Fortnite, create a huge black market of stolen accounts.

In this blog post, I’m gonna share my experience with these kinds of attacks and show features I’ve implemented in one of our project, as well as solutions that didn’t work quite well. Unlike SQL injection, cross-site scripting or any other common vulnerabilities, there is no simple programmatic countermeasure to credential stuffing. Our job is to reject login requests coming from an attacker without sacrificing UX for legitimate users — and it’s not as easy as you might think.

Rate limiting

Rate limiting is popular and often advised defense from automated logins. You can limit login attempts to let’s say 5 per 15 minutes per IP address or lock the account after 3 subsequent failed logins, but it’s not effective in practice. IP based throttling could potentially work, but real attacks are performed using proxy servers or botnets so each request will come from a different IP address. Using Basecamp’s example mentioned above — they’ve been attacked from over 5000 unique addresses. If an attacker has 5000 IPs at his disposal, he can tune his script to avoid hitting the limit and still send requests at a pretty good rate. It can turn out to be really annoying for legitimate users too. Imagine your web app is used by students during lessons — if someone forgets his password and will exceed the quota per school IP, then access for the whole class will be restricted. This method is not completely useless, but has to be implemented wisely and definitely cannot be the only protection. Also locking user’s account after too many incorrect logins won’t bring the desired effect. If only one password is tested for each user, then this rule can be never triggered.

Captcha

Captcha seems to be the perfect way to separate real users from bots. In fact, it is one of the best ways to stop basic attacks and I truly recommend it. It’s definitely a quick win if you’re facing the attack right now and you need an easy and quite effective solution. But there is a caveat — even captcha solving can be automated. There are businesses employing hundreds of people who click on pictures of traffic lights for you for a small compensation. Prices start from a couple of cents per 1000 solved captchas, sometimes even with “service level agreement” and refund policy. Although it raises costs significantly, some websites may be worth of upfront investment and it won’t prevent attacks completely. I’m not mentioning here simple static image captchas, because in the age of neural networks and advanced image recognition they can be broken at a marginal cost.

Like many websites nowadays we decided to use the most widely known service which is reCAPTCHA. From my research, it causes the most trouble to solvers. At the moment you can choose from 3 versions of reCAPTCHA: classic “I’m not a robot“ checkbox, “invisible” which is shown only to suspicious users and the newest “V3” which scores user solely using reputation and behavioral features. I find the second one to be the most suitable for login pages, because it provides a good compromise between UX and accuracy, although I must say I have no experience with “V3” yet.

Advanced Cloudflare rules

Cloudflare is mostly known as protection against DDoS attacks and other common threats, but it turns out to be a very useful tool in mitigation of credential stuffing. At the very beginning, we turned on “I’m under attack” mode to quickly block bot traffic. It works by showing a challenge page before the user visits our website for the first time. It’s a really useful feature because it provides a quick response to any kind of attacks. You have to remember “I’m under attack” affects the whole website, so it causes a little friction to user experience and can lower conversion of marketing campaigns. Fortunately, Cloudflare has custom Page Rules. It allows to set specific conditions when requests should be blocked or additionally checked. By that, I could set the rule that shows challenge, but only on pages which url path starts with “/auth/”. You can also check out their new feature called Firewall Rules which allows to create more flexible restrictions. The advantage of Cloudflare is a great visibility of the Internet’s traffic, so they can easily spot malicious clients and block them.

Implementing two-factor authentication

Since previous solutions are passive in the sense that they don’t require any action from the user, the most reliable protections can be only implemented by users themselves. The best security feature to prevent against account takeover you can offer your users is two-factor authentication. Most popular options are:

  • email confirmation
  • SMS codes
  • time-based one-time codes
  • security keys

Email confirmation is the simplest method and probably shouldn’t be treated as very secure, but it can still be useful for situations when the user has no second factor authentication and login attempt looks suspicious.

SMS codes are also accessible for an average user. Almost every person using the Internet has a phone capable of receiving text messages, so there is no entry barrier. There is a huge downside of this technique though — phone numbers are relatively easy to reroute by cybercriminals. It’s definitely better than nothing, but you should be aware of the threat if your site is high profile. Another disadvantage is the additional cost of sending tokens over SMS.

In my opinion, U2F security key offers the best protection. It’s resistant to phishing attacks, however, it requires a physical device which costs around $20. Native U2F API is available only in Chrome and Opera, but U2F is also compatible with the new WebAuthn standard implemented in almost every new browser. Sadly not many people heard about security keys and even less own at least one. To that point, one-time codes generated by mobile apps like Google Authenticator offer really good balance between security and usability and I recommend to implement them first with physical security key as an upgrade.

Enabling two-factor authentication for regular users means it can be required for accounts with administrative permissions, improving the overall security of your site — a nice side-effect.

Showing advice on how to build great passwords

In the process of making your site more secure, you should also take a look at places where users interact with passwords and enforce (or encourage) good practices in this regard. By doing that we can try to educate and change habits of at least some people.

One of the examples is showing visual clues about building strong passwords. You’ve probably seen many of such complexity rules and they’re annoying or sometimes even stupid. Enough to say that often strong 32-characters, random passwords don’t pass them. So shouldn’t we ditch them and leave only basic minimum length validation? No, we can have awesome complexity rules instead. The best implementation I’ve seen is Dropbox signup page. They’ve created open-source validator called zxcvbn, named after Internet’s famous password. It estimates the strength of a password in a scale from 0 to 4 based on dictionaries and occurrence of predictable patterns. What’s important, it won’t give a high score to P@ssword1 and bZPWaUwH966T7THN won’t be rejected due to lack of special characters.

Another way to convince users that they use weak passwords is to check them against previous data breaches. Facebook or Google have dedicated teams to look for new leaks and proactively notify users with the same credentials. That practice is not feasible for any smaller company unless maintaining a database of breached passwords isn’t your core business. Fortunately, there’s an API for doing that. Project Have I Been Pwned run by security expert Troy Hunt lets anonymously check which passwords were part of known data breaches. It works by passing a 5 character prefix of SHA-1 password hash. In the response, we’ll get a list of suffixes to check — if the hash exists in there, then password was leaked in the past. Here’s an example for string password:

$ echo -n password | shasum -a 1
5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 -
$ curl https://api.pwnedpasswords.com/range/5baa6
003D68EB55068C33ACE09247EE4C639306B:3
012C192B2F16F82EA0EB9EF18D9D539B0DD:1
01330C689E5D64F660D6947A93AD634EF8F:1
...
1E2AAA439972480CEC7F16C795BBB429372:1
1E3687A61BFCE35F69B7408158101C8E414:1
1E4C9B93F3F0682250B6CF8331B7EE68FD8:3645804
1F2B668E8AABEF1C59E9EC6F82E3F3CD786:1
...

We can perform this as an additional validation for signup form, as well as a routine check during every login. It allows you to show your user advice to change his password and set up two-factor authentication.

Another good practice is sending emails after successful login attempts. Some sites send also notifications of failed attempts to let the user know when something bad happens.

Closing loopholes

After we managed to create the login process extremely annoying to attackers, I’ve discovered they try to narrow the scope of their operations by eliminating some ineffectiveness. Correctly implemented login page shouldn’t reveal what was the real reason for rejecting credentials — was it incorrect password or if the account didn’t exist at all. What they did was submitting incomplete signup forms and checking if the email was accepted or not. By doing this they could create a list of registered emails and then limit the number of accounts to check. I’ve made a quick change — email was validated only if every other field was filled. This resulted in many unwanted signups and the only solution was to rebuild signup form to not show this information. You have to evaluate every change you make and check how attackers adapt to them.

Alternatives

Depending on your use case, you can take an alternative approach to the problem. I won’t cover them in details, but some of them are:

  • authenticate users with OAuth using their existing accounts on Facebook, Google, GitHub etc.
  • generate random usernames so there will be no easy way to use credentials lists
  • delegate authentication to a third-party provider

Summary

I hope this short article helped you to better understand credential stuffing. We can expect an increase in popularity of these kinds of attacks because they are really easy to perform. Dedicated tools allow to automate the whole process, which makes programming skills obsolete. I genuinely encourage every developer to assess the risk for their websites. Even if you think your business is not affected, you should consider to implement a captcha on the login page — it’s a low hanging fruit, but it will discourage most of the potential attackers. Monitoring of login attempts is crucial— any drastic change in volume or rate of failures should be the red flag.

Review your own security too — did you happen to reuse passwords? You should get yourself a password manager — 1Password, LastPass, KeePass or any other and change all important passwords today. Set up two factor authentication on every website you can. If you use the Internet actively, then your data is probably already floating around somewhere.

If you enjoyed this post, please hit the clap button below 👏👏👏

You can also follow us on Facebook, Twitter and LinkedIn.

--

--