How to Implement Failed Login Attempt Limits for Nginx Auth_Basic to Prevent Brute Force Attacks


30 views

Nginx's auth_basic module provides simple HTTP authentication, but it lacks built-in mechanisms to limit failed login attempts. This creates a security vulnerability where attackers can brute force credentials through repeated attempts.

We can combine auth_basic with Nginx's limit_req module to implement attempt limiting:


http {
    limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=3r/m;
    
    server {
        location /protected/ {
            auth_basic "Restricted Area";
            auth_basic_user_file /etc/nginx/.htpasswd;
            
            limit_req zone=auth_limit burst=5 nodelay;
            limit_req_status 429;
            
            # Return 429 (Too Many Requests) instead of 401 after limit
            error_page 401 =429 /too_many_requests.html;
        }
    }
}

For more sophisticated protection, integrate Fail2Ban to block IPs after repeated failures:


# /etc/fail2ban/jail.d/nginx-auth.conf
[nginx-auth]
enabled = true
filter = nginx-auth
action = iptables-multiport[name=NoAuthFailures, port="http,https"]
logpath = /var/log/nginx/error.log
maxretry = 3
findtime = 300
bantime = 3600

Ensure your Nginx logs capture authentication attempts:


log_format auth_log '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" "$http_authorization"';
                    
access_log /var/log/nginx/auth.log auth_log;
  • Always use HTTPS with auth_basic to prevent credential sniffing
  • Consider implementing client certificate authentication for sensitive areas
  • Regularly rotate your .htpasswd file and monitor for suspicious activity

While Nginx's auth_basic module provides simple HTTP authentication, it lacks built-in mechanisms to prevent brute force attacks. The default implementation allows unlimited password attempts, making protected resources vulnerable to dictionary attacks.

We'll implement a fail2ban filter combined with Nginx error logging to block IPs after repeated failed attempts. This creates a three-layer defense:

1. Nginx auth_basic (first authentication layer)
2. Fail2ban monitoring (attack detection)
3. IPtables firewall (enforcement)

1. Enable Detailed Error Logging

Add this to your Nginx server block:

error_log /var/log/nginx/auth_errors.log notice;

2. Create Fail2ban Filter

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

[Definition]
failregex = ^ $$error$$ \d+#\d+: \*\d+ user "(?:[^"]+)"?:? (?:password mismatch|was not found in ".*"), client: <HOST>
ignoreregex =

3. Configure Fail2ban Jail

Add to /etc/fail2ban/jail.local:

[nginx-auth]
enabled = true
port = http,https
filter = nginx-auth
logpath = /var/log/nginx/auth_errors.log
maxretry = 3
findtime = 300
bantime = 3600

For additional protection, implement Nginx rate limiting:

limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=5r/m;

server {
    location /protected/ {
        limit_req zone=auth_limit burst=3 nodelay;
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
}

After implementation:

# Test fail2ban
sudo fail2ban-client status nginx-auth

# Check banned IPs
sudo iptables -L -n

For more granular control, use OpenResty with Lua:

access_by_lua_block {
    local redis = require "resty.redis"
    local red = redis:new()
    
    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.log(ngx.ERR, "failed to connect to redis: ", err)
        return
    end
    
    local attempts = red:incr(ngx.var.remote_addr)
    if attempts > 5 then
        ngx.exit(ngx.HTTP_FORBIDDEN)
    end
}