Securing a web application is not trivial. Every feature and especially public exposed forms are potential security breaches. Any breach will eventually be exploited, even on small applications.
For a long time, we have been looking for the best way to protect login and registration forms against brute force attacks and spam bots, often using a combination of techniques.
Today, we are releasing active_hashcash, a gem to protect Ruby on Rails applications forms.
What about current solutions?
IPs rate limiting
This technique limits how often someone can repeat an action within a given timeframe. IPs rate limiting is efficient against brute force or DoS attacks, but not so much against bots or botnets. It might block legitimate users and lock them out of their accounts after too many failed login attempts.
We can use IPs rate limiting carefully. It is not sufficient to protect our applications.
This technique uses fake hidden input fields. If those fake inputs are not left empty on form submission, you can assume a bot filled the form. Honeypot form fields are simple and efficient against dumb bots. A bespoke bot targeting your site will not fall for it. Also, honeypot fields do not help against brute force attacks.
We can use Honeypot fields. It is not sufficient to protect our applications.
Captchas require someone to correctly evaluate and enter a sequence of letters or numbers perceptible in a distorted image displayed on their screen. Captchas are efficient for blocking bots or slowing them down for the least.
Captchas also have some severe drawbacks:
- They are annoying for the legitimate users of your application. Filling a captcha is fastidious and sometimes hard to do correctly. It takes the average person approximately 10 seconds to solve a typical CAPTCHA. This technique receives much criticism, especially from people with disabilities.
- They depend on an external service, which can be slow or down, thus blocking access to your application.
As we want to build more accessible web applications, we would prefer not using captchas.
reCaptcha, the most popular Captcha solution, is Google’s property. Your visitors become Google’s product.
- Google collects and sells your customers' data to advertisers.
- Google uses your visitors’ time to train its AI efforts for free.
Google or any other giant corporation should not own the internet.
We previously made an analytics gem to stop using Google Analytics and to respect our visitors’ privacy.
Moreover, reCaptcha does not block all bots. Some bots can solve captchas. Some even hire low-wage humans to do it for them.
For the same reasons, we would not use reCaptcha.
Another solution is to move behind a third party service such as Cloudflare. The goal of that extra layer is to block bad trafic before it reaches your site. But it makes the Internet more centralized and add an external dependency where you have no leverage. The reverse proxy sees all trafic sent to you. That means, you must have an extremely high trust into this service. Furthemore, it could be a nightmare for some legitimate users depending of their country.
For these reasons, we would not use a third party reverse proxy.
Active Hashcash: An alternative to captchas
We wanted to replace captchas with a user-friendlier solution, and after seeing some brute force attacks against our Rails application monitoring service RorVsWild, we developed the ActiveHashcash gem.
Hashcash is not a new technique. It was created in 1997 by Adam Back. It is used to help detecting email spam and by crypto currencies. It is a proof-of-work algorithm that can be useful as a denial-of-service countermeasure technique. The client has to spend some time solving a complex problem that is easy to verify for the server.
Hashcash is a proof-of-work algorithm
By enabling Active Hashcash on sensitive forms, the browser solves the problem while the user fills out the form. It cannot be submitted as long as the proof of work is not solved. Then the server verifies the proof of work from the hidden field.
Here is a demo on a registration form.
Most humans will not notice Active Hashcash since the problem is solved before they finish filling the form. Others, with less performant devices, will have to wait for a few seconds instead of deciphering hieroglyphics or solving a puzzle.
The complexity can be changed via
ActiveHashcash.bits = 20.
It can also be changed dynamically by overriding the controller's method
Active Hashcash is complementary to rate IP limiting and honeypot field form. We strongly encourage everyone to have all three protections.
Compromises and future improvements
Some might object that botnets don't care about burning CPU cycles. An innocent is probably paying the electricity bill for them. But time is money too. The goal here is to become a difficult target. Eventually, they will look after a less protected one.
Others will retort that it's a lot of wasted energy and not environment friendly. For legitimate people, it's very true and sad. But as it does not concern all requests, the extra consumption is limited. And Bots would have wasted that same energy to attack more sites anyway.
We know Active Hashcash is not perfect and you can help:
- We could have a dynamic complexity. If the same IP is requesting many times a login form, the complexity could be increased gradually. When the IP address is coming from a datacenter, the odd for a legitimate user are lower. In that case it could make sense to increase the complexity...
Good humans, let's unite against bots! Please Contribute