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
}