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.
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->HTTPS, not HTTPS->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->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="admin@example.com"
# 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.