13

I have protected a web folder with Nginx's Auth_Basic module. The problem is, we can try several passwords until it works (brute force attacks). Is there a way to limit the number of failed re-tries?

29

As far as I know, Auth Basic module doesn't support this feature, but you can do this by using Fail2ban.

Testing with a non-existent user, you will see something like belows in the error log:

2012/08/25 10:07:01 [error] 5866#0: *1 no user/password was provided for basic authentication, client: 127.0.0.1, server: localhost, request: "GET /pma HTTP/1.1", host: "localhost:81" 2012/08/25 10:07:04 [error] 5866#0: *1 user "ajfkla" was not found in "/etc/nginx/htpasswd", client: 127.0.0.1, server: localhost, request: "GET /pma HTTP/1.1", host: "localhost:81"

Then create necessary filter:

/etc/fail2ban/filter.d/nginx-auth.conf

[Definition]
failregex = no user/password was provided for basic authentication.*client: <HOST>
              user .* was not found in.*client: <HOST>
              user .* password mismatch.*client: <HOST>
ignoreregex = </host></host></host> 

/etc/fail2ban/jail.conf

[nginx-auth]
enabled = true
filter = nginx-auth
action = iptables[name=NoAuthFailures, port=80, protocol=tcp]
logpath = /var/log/nginx*/*error*.log
bantime = 3600 # 1 hour
maxretry = 3

Testing Fail2Ban rules:

fail2ban-regex /var/log/nginx/localhost.error_log /etc/fail2ban/filter.d/nginx-auth.conf

Failregex
|- Regular expressions:
|  [1] no user/password was provided for basic authentication.*client: <HOST>
|  [2] user .* was not found in.*client: <HOST>
|  [3] user .* password mismatch.*client: <HOST>
|
`- Number of matches:
   [1] 1 match(es)
   [2] 2 match(es)
   [3] 0 match(es)

Ignoreregex
|- Regular expressions:
|
`- Number of matches:

Summary
=======

Addresses found:
[1]
    127.0.0.1 (Sat Aug 25 10:07:01 2012)
[2]
    127.0.0.1 (Sat Aug 25 10:07:04 2012)
    127.0.0.1 (Sat Aug 25 10:07:07 2012)
[3]

PS: Since Fail2ban fetches log files to ban, make sure logpath matches with your configuration.

6

I'm amazed no on else has given this solution/workaround.

Nginx basic-auth and htpasswd support bcrypt password encryption with an optional cost variable. Bcrypt is designed to be slow, thus providing a hard limit on how fast you can attempt different passwords.

When creating your basic auth username/password use

htpasswd -B -C 12 path/to/users.db <username>

With a cost of 12 your server will likely not be able to try passwords more than a few times a second, increase that to say 14 and you'll probably be looking at around 1s per password attempt.

With that configured any reasonable password will be immune to brute force attack even if the attacker tried passwords continuously for years.

E.g. at 10 password attempts per second brute force attack on an 8 character alphanumeric password would take 692,351 years: 62**8 / (10*3600*24*365).

This is much easier to configure and more fool-proof than setting up "intelligent" request limitting.

  • 2
    This would be useful if you could use bcrypted passwords with Nginx's basic auth, but apparently you can't. – keune Jun 20 '17 at 14:34
  • I've tried it, it works absolutely fine for me. Using it in production now. – SColvin Jun 20 '17 at 16:55
  • Not working on vanilla ngix on ubuntu as @keune said – Fabian Thommen Oct 18 '18 at 14:43
  • 3
    Worth mentioning that this will limit password attempts, but only because your server will be overloaded from computing expensive hashes. In a production environment, this isn't probably what you want. – Tomasz P. Szynalski Feb 2 at 20:06
1

I don't believe nginx has any internal facility to do this. The documentation page doesn't suggest it's possible.

You can use Fail2Ban to block IP addresses that have repeated failed login attempts.

The Fail2Ban wiki has some nginx-specific patterns.

Fail2Ban should be available as a package on most of the big distros.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.