Skip to content

Instantly share code, notes, and snippets.

@noxidsoft
Last active January 13, 2025 23:50
Show Gist options
  • Save noxidsoft/b9026e071a42694e9ffbfab6c6dab689 to your computer and use it in GitHub Desktop.
Save noxidsoft/b9026e071a42694e9ffbfab6c6dab689 to your computer and use it in GitHub Desktop.
Web Application Security Configuration Guide (Nginx)

Web Application Security Configuration Guide (Nginx)

Table of Contents

Country Blocking

Installation

# Install GeoIP module and database
sudo apt-get install nginx-module-geoip geoip-database

Configuration

Add to /etc/nginx/nginx.conf:

# Load GeoIP module
load_module modules/ngx_http_geoip_module.so;

http {
    # GeoIP configuration
    geoip_country /usr/share/GeoIP/GeoIP.dat;

    # Create variables for blocked countries
    map $geoip_country_code $allowed_country {
        default yes;
        CN no; # Block China
        RU no; # Block Russia
    }
}

Add to your server block:

server {
    # ... other configurations ...

    # Country blocking
    if ($allowed_country = no) {
        return 403;
    }

    # Custom error page
    error_page 403 /blocked.html;
    location = /blocked.html {
        root /usr/share/nginx/html;
        internal;
    }
}

Rate Limiting

Configuration

Add to nginx.conf or include in http block:

# Rate limiting zones
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

# Limit concurrent connections
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
    # Global rate limiting
    limit_req zone=global burst=20 nodelay;
    limit_conn addr 10;

    # API rate limiting
    location /api/ {
        limit_req zone=api burst=10 nodelay;
        limit_conn addr 5;
        
        # Add rate limit headers
        add_header X-RateLimit-Limit 100;
        add_header X-RateLimit-Remaining $remaining;
        add_header X-RateLimit-Reset $time_local;
    }

    # Login rate limiting
    location ~ /(wp-login\.php|administrator|login) {
        limit_req zone=login burst=3 nodelay;
        limit_conn addr 3;
    }
}

Security Headers

Add to your server block:

# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "geolocation=(), midi=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()";

# CORS headers
add_header Access-Control-Allow-Origin "https://your-domain.com" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;

PHP Configuration

For PHP-FPM with Nginx:

# PHP-FPM configuration
location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    
    # Security headers
    fastcgi_hide_header X-Powered-By;
    fastcgi_hide_header X-CF-Powered-By;
    
    # Timeout settings
    fastcgi_read_timeout 60s;
    fastcgi_send_timeout 60s;
    fastcgi_connect_timeout 60s;
}

Directory Protection

Add to your server block:

# Deny access to hidden files
location ~ /\. {
    deny all;
}

# Protect configuration files
location ~ \.(ini|log|conf)$ {
    deny all;
}

# Protect uploads directory
location /uploads {
    location ~ \.php$ {
        deny all;
    }
}

# Protect sensitive directories
location ~ ^/(config|vendor|logs|temp)/ {
    deny all;
}

# WordPress protection
location ~* wp-config.php {
    deny all;
}

# Joomla protection
location ~* configuration.php {
    deny all;
}

# PHP SaaS protection
location ~ ^/api/ {
    # API specific rules
    if ($request_method !~ ^(GET|POST|PUT|DELETE|OPTIONS)$) {
        return 405;
    }
    
    # Verify JWT token
    if ($http_authorization = "") {
        return 401;
    }
}

ModSecurity for Nginx

Installation

# Install ModSecurity and Nginx connector
sudo apt-get install libmodsecurity3 libmodsecurity-dev
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git
wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar zxvf nginx-1.18.0.tar.gz
cd nginx-1.18.0
./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
make modules
sudo cp objs/ngx_http_modsecurity_module.so /usr/share/nginx/modules/

Configuration

Add to nginx.conf:

load_module modules/ngx_http_modsecurity_module.so;

http {
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsecurity/main.conf;
}

Create /etc/nginx/modsecurity/main.conf:

# Basic configuration
SecRuleEngine On
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecResponseBodyAccess On
SecResponseBodyLimit 524288

# Include OWASP Core Rule Set
Include /etc/nginx/modsecurity/owasp-crs/crs-setup.conf
Include /etc/nginx/modsecurity/owasp-crs/rules/*.conf

# Custom rules
SecRule REQUEST_HEADERS:Content-Type "text/html" \
    "id:1234,phase:1,deny,status:403,msg:'HTML content type not allowed'"

# API protection
SecRule REQUEST_URI "@beginsWith /api/" \
    "chain,phase:1,id:4000,deny,status:401,msg:'API authentication required'"
SecRule REQUEST_HEADERS:Authorization "^$"

# Rate limiting
SecRule &ARGS "@eq 0" "phase:2,id:4100,nolog,pass"
SecRule REQUEST_URI "@beginsWith /api/" \
    "phase:1,id:4101,nolog,pass,setvar:ip.api_count=+1,expirevar:ip.api_count=60"
SecRule IP:API_COUNT "@gt 100" \
    "phase:1,id:4102,deny,status:429,msg:'API rate limit exceeded'"

Fail2ban Configuration

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

[Definition]
failregex = ^<HOST> .* "(?:GET|POST) .*(?:/wp-login\.php|/administrator|/login).*" (?:401|403)
            ^<HOST> .* "(?:GET|POST|PUT|DELETE) /api/.*" (?:401|403|429)
ignoreregex =

Add to /etc/fail2ban/jail.local:

[nginx-auth]
enabled = true
filter = nginx-auth
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 300
bantime = 3600

SSL/TLS Configuration

Add to server block:

# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;

Maintenance

Log Monitoring

# Monitor Nginx access logs
tail -f /var/log/nginx/access.log | grep "403\|401\|429"

# Monitor error logs
tail -f /var/log/nginx/error.log

# Monitor ModSecurity logs
tail -f /var/log/modsec_audit.log

# Monitor Fail2ban logs
tail -f /var/log/fail2ban.log

Testing

After implementing these measures:

  1. Test rate limiting:
# Test API rate limits
for i in {1..110}; do 
    curl -H "Authorization: Bearer test" https://your-domain.com/api/endpoint
done

# Test ModSecurity
curl -H "Content-Type: text/html" https://your-domain.com/api/endpoint
  1. Test country blocking using a VPN

  2. Test SSL configuration:

curl https://www.ssllabs.com/ssltest/analyze.html?d=your-domain.com

Important Notes

  • Always backup configuration files before making changes
  • Test in staging first
  • Monitor logs for false positives
  • Keep modules and rules updated
  • Use strong SSL/TLS configuration
  • Implement proper authentication
  • Regular security audits
  • DDoS protection
  • Regular backups
  • Access control

Need help? Create an issue or contact your system administrator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment