How To limit Nginx Auth_Basic re-tries?

Solution 1:

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.

Solution 2:

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.

Solution 3:

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.