Chuyển đến nội dung chính

Bài 9: Security trong NGINX

Bài học về Security trong Nginx - rate limiting với limit_req/limit_conn, IP blocking, basic authentication, ModSecurity WAF integration, chống DDoS, secure headers (CSP, X-Frame-Options, CORS). Hướng dẫn bảo vệ server khỏi attacks, best practices và security hardening cho production.

🔒 DevSecOps — Bài 9 Bài 9: Security trong NGINX

Nginx từ Cơ bản đến Nâng cao

Phần 3: Security & Performance

xdev.asia

1. Rate Limiting với limit_req và limit_conn

Rate limiting bảo vệ server khỏi abuse, brute-force attacks và DDoS bằng cách giới hạn số requests từ một client.

1.1. Rate Limiting Basics (limit_req)

http {
    # Define rate limit zone
    # $binary_remote_addr: Client IP address (binary format, saves memory)
    # zone=mylimit:10m: Zone name "mylimit", 10MB memory (~160,000 IPs)
    # rate=10r/s: 10 requests per second
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
    listen 80;
    server_name example.com;
    
    location / {
        # Apply rate limit
        limit_req zone=mylimit;
        
        proxy_pass http://backend;
    }
}

}

Rate values:

# Per second
limit_req_zone $binary_remote_addr zone=persec:10m rate=1r/s;   # 1 req/sec
limit_req_zone $binary_remote_addr zone=persec:10m rate=10r/s;  # 10 req/sec
limit_req_zone $binary_remote_addr zone=persec:10m rate=100r/s; # 100 req/sec

Per minute

limit_req_zone $binary_remote_addr zone=permin:10m rate=30r/m; # 30 req/min limit_req_zone $binary_remote_addr zone=permin:10m rate=120r/m; # 120 req/min

Very restrictive (login pages)

limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; # 5 req/min

1.2. Burst và Nodelay

http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

server {
    location / {
        # Allow burst of 20 requests
        limit_req zone=mylimit burst=20;
        
        # Without nodelay: excess requests delayed
        # With nodelay: excess requests processed immediately
        # limit_req zone=mylimit burst=20 nodelay;
        
        proxy_pass http://backend;
    }
}

}

Burst explained:

rate=10r/s burst=20

Request pattern: Time 0s: 30 requests arrive simultaneously

Without burst:

  • Request 1-10: Processed immediately
  • Request 11-30: Rejected (503 error)

With burst=20:

  • Request 1-10: Processed immediately
  • Request 11-20: Queued (delayed)
  • Request 21-30: Rejected

With burst=20 nodelay:

  • Request 1-30: All processed immediately

  • Client "borrows" from future quota

  • Next 20 seconds: Client blocked (paying back debt)

1.3. Multiple Rate Limit Zones

http {

General API rate limit

limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;

Login rate limit (strict)

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

Search rate limit

limit_req_zone $binary_remote_addr zone=search:10m rate=10r/s;

Download rate limit

limit_req_zone $binary_remote_addr zone=download:10m rate=2r/s;

server { listen 80; server_name example.com;

  # General pages
  location / {
      limit_req zone=api burst=50 nodelay;
      proxy_pass http://backend;
  }
  
  # Login endpoint (very strict)
  location /api/login {
      limit_req zone=login burst=3 nodelay;
      proxy_pass http://backend;
  }
  
  # Search endpoint
  location /api/search {
      limit_req zone=search burst=20 nodelay;
      proxy_pass http://backend;
  }
  
  # Download files
  location /downloads/ {
      limit_req zone=download burst=5;
      root /var/www/downloads;
  }

} }

1.4. Connection Limiting (limit_conn)

http {

Limit concurrent connections per IP

limit_conn_zone $binary_remote_addr zone=addr:10m;

Limit concurrent connections per server

limit_conn_zone $server_name zone=perserver:10m;

server { listen 80; server_name example.com;

  # Max 10 concurrent connections per IP
  limit_conn addr 10;
  
  # Max 1000 concurrent connections to this server
  limit_conn perserver 1000;
  
  location /downloads/ {
      # Stricter limit for downloads
      limit_conn addr 2;
      root /var/www/downloads;
  }

} }

1.5. Custom Rate Limit Keys

http {

Rate limit by API key

limit_req_zone $http_x_api_key zone=apikey:10m rate=1000r/s;

Rate limit by cookie (user-based)

limit_req_zone $cookie_user_id zone=userid:10m rate=50r/s;

Rate limit by URI

limit_req_zone $request_uri zone=uri:10m rate=10r/s;

Combined key (IP + User-Agent)

map $binary_remote_addr$http_user_agent $limit_key { default $binary_remote_addr$http_user_agent; } limit_req_zone $limit_key zone=combined:10m rate=20r/s;

server { location /api/ { # Apply appropriate limit limit_req zone=apikey burst=100 nodelay; proxy_pass http://backend; } } }

1.6. Rate Limit Status Codes

http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

Custom status code for rate limit

limit_req_status 429; # Default: 503

server { location / { limit_req zone=mylimit burst=20 nodelay;

      # Custom error page
      error_page 429 /rate_limit.html;
      
      proxy_pass http://backend;
  }
  
  location = /rate_limit.html {
      internal;
      root /var/www/errors;
  }

} }

1.7. Bypass Rate Limiting

http {

Whitelist certain IPs

geo $limit { default 1; 10.0.0.0/8 0; # Internal network 192.168.1.100 0; # Admin IP }

map $limit $limit_key { 0 ""; 1 $binary_remote_addr; }

limit_req_zone $limit_key zone=mylimit:10m rate=10r/s;

server { location / { limit_req zone=mylimit burst=20 nodelay; proxy_pass http://backend; } } }

1.8. Complete Rate Limiting Example

http {

Define multiple zones for different purposes

General API - moderate limit

limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;

Authentication - very strict

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

Search - medium limit

limit_req_zone $binary_remote_addr zone=search:10m rate=10r/s;

File uploads - strict

limit_req_zone $binary_remote_addr zone=upload:10m rate=2r/m;

Connection limits

limit_conn_zone $binary_remote_addr zone=addr:10m; limit_conn_zone $server_name zone=perserver:10m;

Custom status code

limit_req_status 429; limit_conn_status 429;

Log rate limit events

limit_req_log_level warn;

server { listen 443 ssl http2; server_name api.example.com;

  # Global connection limit
  limit_conn addr 20;
  limit_conn perserver 10000;
  
  # General API endpoints
  location /api/ {
      limit_req zone=api burst=200 nodelay;
      
      proxy_pass http://backend;
      proxy_set_header X-Real-IP $remote_addr;
  }
  
  # Authentication endpoints (strict)
  location ~ ^/api/(login|register|reset-password) {
      limit_req zone=auth burst=3;
      
      proxy_pass http://backend;
  }
  
  # Search endpoint
  location /api/search {
      limit_req zone=search burst=30 nodelay;
      
      proxy_pass http://backend;
  }
  
  # File upload (very strict)
  location /api/upload {
      limit_req zone=upload burst=1;
      limit_conn addr 1;
      
      client_max_body_size 100M;
      proxy_pass http://backend;
  }
  
  # Rate limit error page
  error_page 429 /429.html;
  location = /429.html {
      internal;
      default_type application/json;
      return 429 '{"error":"Too many requests","retry_after":"60"}';
  }

} }


2. Chặn IP với deny/allow

2.1. Basic IP Blocking

server {
listen 80;
server_name example.com;

Block specific IP

deny 192.168.1.100;

Block IP range

deny 192.168.1.0/24;

Block specific IPs

deny 10.0.0.5; deny 10.0.0.10;

Allow all others

allow all;

location / { root /var/www/html; } }

2.2. Whitelist Approach

server {
listen 80;
server_name admin.example.com;

Allow specific IPs only

allow 192.168.1.100; allow 10.0.0.0/8; allow 2001:db8::/32; # IPv6

Deny everyone else

deny all;

location / { root /var/www/admin; } }

2.3. Location-specific Blocking

server {
listen 80;
server_name example.com;

Public access

location / { root /var/www/html; }

Admin area - restricted

location /admin/ { allow 192.168.1.0/24; allow 10.0.0.100; deny all;

  proxy_pass http://admin_backend;

}

API - whitelist only

location /api/ { allow 203.0.113.0/24; deny all;

  proxy_pass http://api_backend;

} }

2.4. Geo-based Blocking

http {

Define geo blocks

geo $allowed_country { default no;

  # US IP ranges
  192.0.2.0/24 yes;
  198.51.100.0/24 yes;
  
  # EU IP ranges
  203.0.113.0/24 yes;

}

server { listen 80; server_name example.com;

  if ($allowed_country = no) {
      return 403 "Access denied from your location";
  }
  
  location / {
      root /var/www/html;
  }

} }

2.5. Block Bad Bots

http {

Define bad bots

map $http_user_agent $bad_bot { default 0; ~malicious 1; ~scrapy 1; ~crawler 1; ~spider 1; ~bot 1; ~curl 1; ~*wget 1; }

server { listen 80; server_name example.com;

  if ($bad_bot) {
      return 403 "Forbidden";
  }
  
  location / {
      root /var/www/html;
  }

} }

2.6. Dynamic IP Blocking

Create blocklist file:

# /etc/nginx/blockips.conf
deny 192.168.1.50;
deny 10.0.0.25;
deny 203.0.113.100;

Include in Nginx:

http {

Include blocklist

include /etc/nginx/blockips.conf;

server { listen 80; server_name example.com;

  location / {
      root /var/www/html;
  }

} }

Script để block IP:

#!/bin/bash

block_ip.sh

IP=$1 BLOCKLIST="/etc/nginx/blockips.conf"

if [ -z "$IP" ]; then echo "Usage: $0 <IP_ADDRESS>" exit 1 fi

Add IP to blocklist

echo "deny $IP;" >> $BLOCKLIST

Reload Nginx

nginx -t && systemctl reload nginx

echo "Blocked IP: $IP"

2.7. Fail2Ban Integration

Install Fail2Ban:

sudo apt install fail2ban

Configure Fail2Ban for Nginx:

# /etc/fail2ban/jail.local
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600

[nginx-limit-req] enabled = true port = http,https filter = nginx-limit-req logpath = /var/log/nginx/error.log maxretry = 10 bantime = 3600

[nginx-badbots] enabled = true port = http,https filter = nginx-badbots logpath = /var/log/nginx/access.log maxretry = 2 bantime = 86400

Create filter:

# /etc/fail2ban/filter.d/nginx-limit-req.conf
[Definition]
failregex = limiting requests, excess: .* by zone .*, client: <HOST>
ignoreregex =

Restart Fail2Ban:

sudo systemctl restart fail2ban
sudo fail2ban-client status nginx-limit-req

3. Basic Authentication

3.1. Setup Basic Auth

Create password file:

# Install htpasswd utility
sudo apt install apache2-utils

Create user

sudo htpasswd -c /etc/nginx/.htpasswd admin

Add more users

sudo htpasswd /etc/nginx/.htpasswd user2

Set permissions

sudo chmod 644 /etc/nginx/.htpasswd

Configure Nginx:

server {
listen 80;
server_name example.com;

location /admin/ {
    # Enable basic auth
    auth_basic "Restricted Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    root /var/www/admin;
}

}

3.2. Location-specific Auth

server {
listen 80;
server_name example.com;

# Public area
location / {
    root /var/www/html;
}

# Protected admin area
location /admin/ {
    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
    root /var/www/admin;
}

# Protected API
location /api/ {
    auth_basic "API Access";
    auth_basic_user_file /etc/nginx/.htpasswd_api;
    proxy_pass http://backend;
}

}

3.3. Conditional Authentication

http {
# Whitelist IPs that don't need auth
geo $authentication {
default "Restricted";
192.168.1.0/24 "off";
10.0.0.0/8 "off";
}

server {
    listen 80;
    
    location /admin/ {
        auth_basic $authentication;
        auth_basic_user_file /etc/nginx/.htpasswd;
        
        root /var/www/admin;
    }
}

}

3.4. Multiple Auth Methods

server {
listen 80;
server_name example.com;

location /admin/ {
    # Require IP whitelist AND password
    satisfy all;
    
    # IP whitelist
    allow 192.168.1.0/24;
    deny all;
    
    # Basic auth
    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    root /var/www/admin;
}

location /staff/ {
    # Require IP whitelist OR password
    satisfy any;
    
    allow 192.168.1.0/24;
    deny all;
    
    auth_basic "Staff Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    root /var/www/staff;
}

}

3.5. JWT Authentication

http {
# Verify JWT token
map $http_authorization $jwt_valid {
default 0;
~^Bearer\s+([A-Za-z0-9-=]+.[A-Za-z0-9-=]+.?[A-Za-z0-9-_.+/=]) 1;
}

server {
    listen 80;
    
    location /api/ {
        if ($jwt_valid = 0) {
            return 401 '{"error":"Invalid or missing token"}';
        }
        
        # Forward token to backend for validation
        proxy_pass http://backend;
        proxy_set_header Authorization $http_authorization;
    }
}

}


4. ModSecurity WAF Integration

ModSecurity là Web Application Firewall (WAF) bảo vệ khỏi common attacks.

4.1. Install ModSecurity

# Ubuntu/Debian
sudo apt update
sudo apt install libmodsecurity3 libmodsecurity-dev

Clone connector

cd /opt sudo git clone https://github.com/SpiderLabs/ModSecurity-nginx.git

Get Nginx version

nginx -v

Download Nginx source (same version)

cd /opt wget http://nginx.org/download/nginx-1.24.0.tar.gz tar -xzf nginx-1.24.0.tar.gz cd nginx-1.24.0

Compile with ModSecurity module

./configure --add-dynamic-module=/opt/ModSecurity-nginx make modules

Copy module

sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/

Load module in nginx.conf

sudo nano /etc/nginx/nginx.conf

Add at top:

load_module modules/ngx_http_modsecurity_module.so;

4.2. Configure ModSecurity

# Create config directory
sudo mkdir /etc/nginx/modsec
cd /etc/nginx/modsec

Download OWASP Core Rule Set

sudo git clone https://github.com/coreruleset/coreruleset.git

Copy config files

sudo cp /opt/ModSecurity/modsecurity.conf-recommended modsecurity.conf sudo cp /opt/ModSecurity/unicode.mapping .

Edit config

sudo nano modsecurity.conf

Change:

SecRuleEngine On # Enable rules SecAuditLog /var/log/modsec_audit.log

4.3. Enable in Nginx

http {
# Load ModSecurity module
load_module modules/ngx_http_modsecurity_module.so;

server {
    listen 80;
    server_name example.com;
    
    # Enable ModSecurity
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;
    
    location / {
        proxy_pass http://backend;
    }
}

}

4.4. OWASP Core Rule Set

# Main config file
sudo nano /etc/nginx/modsec/main.conf

Add:

Include /etc/nginx/modsec/modsecurity.conf Include /etc/nginx/modsec/coreruleset/crs-setup.conf Include /etc/nginx/modsec/coreruleset/rules/*.conf

Update Nginx config:

server {
listen 80;
server_name example.com;

modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;

location / {
    proxy_pass http://backend;
}

}

4.5. Custom ModSecurity Rules

# /etc/nginx/modsec/custom_rules.conf

Block SQL injection attempts

SecRule ARGS "@detectSQLi"
"id:1001,phase:2,deny,status:403,msg:'SQL Injection Detected'"

Block XSS attempts

SecRule ARGS "@detectXSS"
"id:1002,phase:2,deny,status:403,msg:'XSS Detected'"

Block file upload exploits

SecRule FILES "@rx .(?:php|exe|sh)$"
"id:1003,phase:2,deny,status:403,msg:'Dangerous file upload'"

Rate limiting per IP

SecRule IP:REQUEST_COUNT "@gt 100"
"id:1004,phase:1,deny,status:429,msg:'Rate limit exceeded'"


5. Chống DDoS Cơ bản

5.1. Connection Limits

http {
# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;

# Limit request rate
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;

server {
    listen 80;
    server_name example.com;
    
    # Apply limits
    limit_conn addr 10;
    limit_req zone=req burst=20 nodelay;
    
    location / {
        proxy_pass http://backend;
    }
}

}

5.2. Timeout Protection

http {
# Short timeouts to free resources quickly
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;

# Limit request body size
client_max_body_size 1M;

server {
    listen 80;
    
    location / {
        proxy_pass http://backend;
        
        # Backend timeouts
        proxy_connect_timeout 5s;
        proxy_send_timeout 10s;
        proxy_read_timeout 10s;
    }
}

}

5.3. Block Attack Patterns

http {
# Block bad methods
map $request_method $bad_method {
default 0;
~*^(TRACE|DELETE|PUT) 1;
}

# Block suspicious URIs
map $request_uri $bad_uri {
    default 0;
    ~*\.\./\.\. 1;  # Directory traversal
    ~*\.(bash|git|svn|env|log) 1;
    ~*\.(php|asp|aspx|jsp)\. 1;  # Double extensions
}

# Block bad referers
map $http_referer $bad_referer {
    default 0;
    ~*porn 1;
    ~*casino 1;
    ~*viagra 1;
}

server {
    listen 80;
    
    if ($bad_method) {
        return 405;
    }
    
    if ($bad_uri) {
        return 403;
    }
    
    if ($bad_referer) {
        return 403;
    }
    
    location / {
        proxy_pass http://backend;
    }
}

}

5.4. SYN Flood Protection

# System-level protection
sudo nano /etc/sysctl.conf

Add:

net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 2048 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2

Apply

sudo sysctl -p

5.5. Slowloris Protection

http {
# Protect against slowloris
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;

# Limit header size
large_client_header_buffers 2 1k;

# Request body limits
client_body_buffer_size 1k;
client_max_body_size 1M;

server {
    listen 80;
    
    location / {
        proxy_pass http://backend;
    }
}

}

5.6. Complete DDoS Protection

http {
# Connection limits
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn_zone $server_name zone=perserver:10m;

# Rate limits
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=strict:10m rate=1r/s;

# Timeouts
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;
keepalive_timeout 30s;

# Size limits
client_body_buffer_size 1k;
client_max_body_size 1M;
client_header_buffer_size 1k;
large_client_header_buffers 2 1k;

# Bad patterns
map $request_method $bad_method {
    default 0;
    ~*^(TRACE|DELETE|PUT|PATCH) 1;
}

map $http_user_agent $bad_bot {
    default 0;
    "" 1;  # Empty user agent
    ~*bot 1;
    ~*crawler 1;
    ~*spider 1;
}

server {
    listen 80;
    server_name example.com;
    
    # Connection limits
    limit_conn addr 20;
    limit_conn perserver 10000;
    
    # Rate limits
    limit_req zone=general burst=50 nodelay;
    
    # Block bad requests
    if ($bad_method) {
        return 405;
    }
    
    if ($bad_bot) {
        return 403;
    }
    
    # General content
    location / {
        proxy_pass http://backend;
        
        proxy_connect_timeout 5s;
        proxy_send_timeout 10s;
        proxy_read_timeout 10s;
    }
    
    # Sensitive endpoints (stricter)
    location /api/admin/ {
        limit_req zone=strict burst=5;
        limit_conn addr 5;
        
        allow 192.168.1.0/24;
        deny all;
        
        proxy_pass http://backend;
    }
}

}


6. Secure Headers

6.1. X-Frame-Options

Prevents clickjacking attacks.

server {
listen 443 ssl http2;
server_name example.com;

# Prevent framing (clickjacking)
add_header X-Frame-Options "SAMEORIGIN" always;

# Or completely deny framing:
# add_header X-Frame-Options "DENY" always;

# Or allow specific origin:
# add_header X-Frame-Options "ALLOW-FROM https://trusted.com" always;

}

6.2. X-Content-Type-Options

Prevents MIME-type sniffing.

server {
listen 443 ssl http2;

# Prevent MIME sniffing
add_header X-Content-Type-Options "nosniff" always;

}

6.3. X-XSS-Protection

Enables browser XSS filter.

server {
listen 443 ssl http2;

# Enable XSS filter
add_header X-XSS-Protection "1; mode=block" always;

}

6.4. Referrer-Policy

Controls referrer information.

server {
listen 443 ssl http2;

# Referrer policy
add_header Referrer-Policy "no-referrer-when-downgrade" always;

# Options:
# no-referrer - Never send referrer
# no-referrer-when-downgrade - Send on HTTPS-&gt;HTTPS, not HTTPS-&gt;HTTP
# origin - Send only origin
# origin-when-cross-origin - Full URL for same origin, origin for cross-origin
# same-origin - Send only for same origin
# strict-origin - Send origin for HTTPS-&gt;HTTPS
# strict-origin-when-cross-origin - Recommended
# unsafe-url - Always send full URL

}

6.5. Content-Security-Policy (CSP)

Powerful defense against XSS and injection attacks.

server {
listen 443 ssl http2;
server_name example.com;

# Basic CSP
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self';" always;

}

CSP directives explained:

# Strict CSP (most secure)
add_header Content-Security-Policy "
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self';
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
" always;

Moderate CSP (allows CDNs)

add_header Content-Security-Policy " default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://code.jquery.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self'; " always;

Report-only mode (testing)

add_header Content-Security-Policy-Report-Only " default-src 'self'; report-uri /csp-report; " always;

6.6. Permissions-Policy

Controls browser features.

server {
listen 443 ssl http2;

# Permissions policy (formerly Feature-Policy)
add_header Permissions-Policy "
    geolocation=(self),
    microphone=(),
    camera=(),
    payment=(),
    usb=(),
    magnetometer=(),
    gyroscope=(),
    accelerometer=()
" always;

}

6.7. CORS Headers

server {
listen 443 ssl http2;
server_name api.example.com;

location /api/ {
    # CORS headers
    add_header Access-Control-Allow-Origin "https://app.example.com" always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
    add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
    add_header Access-Control-Allow-Credentials "true" always;
    add_header Access-Control-Max-Age "3600" always;
    
    # Handle preflight OPTIONS requests
    if ($request_method = OPTIONS) {
        return 204;
    }
    
    proxy_pass http://backend;
}

}

Wildcard CORS (use with caution):

location /api/ {
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;

if ($request_method = OPTIONS) {
    return 204;
}

proxy_pass http://backend;

}

6.8. Complete Security Headers

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;

# SSL configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;

# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net;
    style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
    img-src 'self' data: https:;
    font-src 'self' https://fonts.gstatic.com;
    connect-src 'self' https://api.example.com;
    frame-ancestors 'self';
    base-uri 'self';
    form-action 'self';
" always;

add_header Permissions-Policy "
    geolocation=(self),
    microphone=(),
    camera=(),
    payment=(),
    usb=()
" always;

# Hide Nginx version
server_tokens off;

# Additional security
more_clear_headers Server;
more_clear_headers X-Powered-By;

root /var/www/html;
index index.html;

location / {
    try_files $uri $uri/ =404;
}

}


7. Complete Security Configuration

7.1. Production-Ready Security Setup

# /etc/nginx/nginx.conf

user nginx; worker_processes auto; worker_rlimit_nofile 65535; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid;

events { worker_connections 4096; use epoll; multi_accept on; }

http { include /etc/nginx/mime.types; default_type application/octet-stream;

# Hide version
server_tokens off;
more_clear_headers Server;

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

# Connection limiting
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn_zone $server_name zone=perserver:10m;

# Status codes
limit_req_status 429;
limit_conn_status 429;

# IP blocklist
include /etc/nginx/blockips.conf;

# Bad bot blocking
map $http_user_agent $bad_bot {
    default 0;
    "" 1;
    ~*bot 1;
    ~*crawler 1;
    ~*scraper 1;
}

# Bad methods
map $request_method $bad_method {
    default 0;
    ~*^(TRACE|DELETE|PUT) 1;
}

# Timeouts
client_body_timeout 12s;
client_header_timeout 12s;
send_timeout 10s;
keepalive_timeout 65s;

# Size limits
client_body_buffer_size 128k;
client_max_body_size 10M;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;

# Logging
log_format security '$remote_addr - $remote_user [$time_local] '
                   '"$request" $status $body_bytes_sent '
                   '"$http_referer" "$http_user_agent" '
                   'rt=$request_time uct=$upstream_connect_time '
                   'uht=$upstream_header_time urt=$upstream_response_time';

access_log /var/log/nginx/access.log security;

# Include configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

}

7.2. Secure Site Configuration

# /etc/nginx/sites-available/example.com

Upstream

upstream backend { least_conn; server backend1:8080 max_fails=3 fail_timeout=30s; server backend2:8080 max_fails=3 fail_timeout=30s; keepalive 32; }

HTTP -> HTTPS redirect

server { listen 80; listen [::]:80; server_name example.com www.example.com;

# Block bad bots
if ($bad_bot) {
    return 403;
}

# Block bad methods
if ($bad_method) {
    return 405;
}

location /.well-known/acme-challenge/ {
    root /var/www/html;
}

location / {
    return 301 https://example.com$request_uri;
}

}

Main HTTPS server

server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com;

root /var/www/html;
index index.html;

# SSL
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_stapling on;
ssl_stapling_verify on;

# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;

# Connection limits
limit_conn addr 20;
limit_conn perserver 10000;

# Rate limiting
limit_req zone=general burst=50 nodelay;

# Block bad requests
if ($bad_bot) {
    return 403;
}

if ($bad_method) {
    return 405;
}

# Main location
location / {
    try_files $uri $uri/ =404;
}

# API endpoints
location /api/ {
    limit_req zone=api burst=200 nodelay;
    
    proxy_pass http://backend/;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

# Login endpoint (strict rate limit)
location /api/login {
    limit_req zone=login burst=3;
    
    proxy_pass http://backend/api/login;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

# Admin area (IP whitelist + auth)
location /admin/ {
    satisfy all;
    
    allow 192.168.1.0/24;
    allow 10.0.0.100;
    deny all;
    
    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    proxy_pass http://backend/admin/;
}

# Static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

# Deny hidden files
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

# Error pages
error_page 403 /403.html;
error_page 404 /404.html;
error_page 429 /429.html;
error_page 500 502 503 504 /50x.html;

}


8. Security Testing và Monitoring

8.1. Security Scanners

# Nikto vulnerability scanner
nikto -h https://example.com

OWASP ZAP

zap-cli quick-scan https://example.com

SSL Labs test

Visit: https://www.ssllabs.com/ssltest/

Security Headers test

Visit: https://securityheaders.com

8.2. Monitor Attacks

#!/bin/bash

monitor_attacks.sh

LOG_FILE="/var/log/nginx/access.log" ERROR_LOG="/var/log/nginx/error.log"

echo "Security Monitoring" echo "=================="

Failed login attempts

echo "Failed Login Attempts:" grep "/api/login" $LOG_FILE | grep -c " 401 "

Rate limit hits

echo "Rate Limit Hits:" grep "limiting requests" $ERROR_LOG | wc -l

Blocked IPs

echo "Blocked IPs:" grep " 403 " $LOG_FILE | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

Suspicious URLs

echo "Suspicious URLs:" grep -E "../|.php.|.env|.git" $LOG_FILE | wc -l

Top 403 URLs

echo "Top 403 URLs:" grep " 403 " $LOG_FILE | awk '{print $7}' | sort | uniq -c | sort -rn | head -10

8.3. Real-time Alerts

#!/bin/bash

security_alert.sh

THRESHOLD=100 EMAIL="[email protected]"

Check rate limit hits

RATE_LIMIT_HITS=$(grep "limiting requests" /var/log/nginx/error.log | wc -l)

if [ $RATE_LIMIT_HITS -gt $THRESHOLD ]; then echo "ALERT: $RATE_LIMIT_HITS rate limit hits detected" | mail -s "Security Alert" $EMAIL fi

Check for SQL injection attempts

SQL_ATTEMPTS=$(grep -E "union.*select|concat(|load_file" /var/log/nginx/access.log | wc -l)

if [ $SQL_ATTEMPTS -gt 0 ]; then echo "ALERT: $SQL_ATTEMPTS SQL injection attempts detected" | mail -s "SQL Injection Alert" $EMAIL fi


Tổng kết

Trong bài này, bạn đã học:

  • ✅ Rate limiting với limit_req và limit_conn
  • ✅ IP blocking và whitelisting
  • ✅ Basic authentication và JWT
  • ✅ ModSecurity WAF integration
  • ✅ DDoS protection techniques
  • ✅ Secure headers (CSP, X-Frame-Options, CORS)
  • ✅ Security testing và monitoring

Security checklist:

  • ✅ Rate limiting enabled
  • ✅ IP blocklist configured
  • ✅ Authentication on sensitive areas
  • ✅ All security headers present
  • ✅ SSL/TLS properly configured
  • ✅ Fail2Ban integrated
  • ✅ Regular security audits
  • ✅ Monitoring và alerting active

Bài tiếp theo: Rewrite và Redirects - regex patterns, return vs rewrite directives, location matching, try_files và conditional redirects để quản lý URL routing effectively.