Nginx 安全配置与防护
Security Configuration and Hardening
概述
安全是Web服务器配置的重要环节。Nginx提供了多种安全机制来保护Web应用免受常见攻击。本文将详细介绍Nginx的安全配置方法、防护策略和最佳实践。
1. 基础安全配置
1.1 隐藏服务器信息
http {
# 隐藏Nginx版本号
server_tokens off;
# 自定义Server头部
more_set_headers "Server: WebServer";
server {
listen 80;
server_name example.com;
# 隐藏PHP版本信息
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 隐藏PHP头部
fastcgi_hide_header X-Powered-By;
}
# 移除敏感头部
location / {
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-AspNetMvc-Version;
}
}
}
1.2 安全头部配置
server {
listen 443 ssl http2;
server_name secure.example.com;
# SSL配置
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# 安全头部
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 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# 内容安全策略
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
" always;
# 权限策略
add_header Permissions-Policy "
camera=(),
microphone=(),
geolocation=(),
payment=(),
usb=()
" always;
location / {
root /var/www/secure;
try_files $uri $uri/ =404;
}
}
2. 访问控制
2.1 IP白名单和黑名单
http {
# 定义可信IP范围
geo $trusted_ip {
default 0;
192.168.1.0/24 1;
10.0.0.0/8 1;
172.16.0.0/12 1;
}
# 黑名单IP
map $remote_addr $blocked_ip {
default 0;
~^192\.168\.100\. 1; # 阻止特定网段
~^203\.0\.113\. 1; # 阻止特定网段
1.2.3.4 1; # 阻止特定IP
}
server {
listen 80;
server_name example.com;
# 阻止黑名单IP
if ($blocked_ip) {
return 403 "Access denied";
}
# 管理区域只允许可信IP
location /admin/ {
if (!$trusted_ip) {
return 403 "Admin access denied";
}
root /var/www/admin;
try_files $uri $uri/ =404;
}
# 普通访问
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
}
}
2.2 基于地理位置的访问控制
http {
# GeoIP配置
geoip_country /usr/share/GeoIP/GeoIP.dat;
geoip_city /usr/share/GeoIP/GeoLiteCity.dat;
# 允许的国家
map $geoip_country_code $allowed_country {
default no;
US yes;
CA yes;
GB yes;
DE yes;
FR yes;
JP yes;
AU yes;
}
# 阻止的国家
map $geoip_country_code $blocked_country {
default no;
CN yes; # 根据需要调整
RU yes;
KP yes;
}
server {
listen 80;
server_name geo.example.com;
# 阻止特定国家
if ($blocked_country = yes) {
return 403 "Access from your country is not allowed";
}
# 某些页面只允许特定国家
location /restricted/ {
if ($allowed_country = no) {
return 403 "This content is not available in your region";
}
root /var/www/restricted;
try_files $uri $uri/ =404;
}
location / {
# 记录地理信息
access_log /var/log/nginx/geo_access.log '$remote_addr - [$time_local] "$request" $status $body_bytes_sent "$geoip_country_name" "$geoip_city"';
root /var/www/html;
try_files $uri $uri/ =404;
}
}
}
3. 防护措施
3.1 DDoS防护
http {
# 限制连接数
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
limit_conn_zone $server_name zone=conn_limit_per_server:10m;
# 限制请求频率
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
limit_req_zone $server_name zone=req_limit_per_server:10m rate=100r/s;
# 大文件上传限制
limit_req_zone $binary_remote_addr zone=upload_limit:10m rate=1r/m;
server {
listen 80;
server_name protected.example.com;
# 应用连接限制
limit_conn conn_limit_per_ip 10;
limit_conn conn_limit_per_server 1000;
# 应用请求频率限制
limit_req zone=req_limit_per_ip burst=20 nodelay;
# 不同路径的不同限制
location / {
limit_req zone=req_limit_per_ip burst=10 nodelay;
root /var/www/html;
try_files $uri $uri/ =404;
}
location /api/ {
limit_req zone=req_limit_per_ip burst=5 nodelay;
proxy_pass http://api-backend;
}
location /upload/ {
limit_req zone=upload_limit burst=3 nodelay;
client_max_body_size 100m;
proxy_pass http://upload-backend;
}
# 限制状态码处理
error_page 429 @rate_limit;
location @rate_limit {
return 200 '{"error":"Rate limit exceeded","retry_after":60}';
add_header Content-Type application/json;
add_header Retry-After 60;
}
}
}
3.2 Bot防护
http {
# 恶意Bot检测
map $http_user_agent $is_bot {
default 0;
~*bot 1;
~*crawler 1;
~*spider 1;
~*scraper 1;
~*wget 1;
~*curl 1;
~*python 1;
~*java 1;
~*apache 1;
~*httpclient 1;
}
# 好的Bot白名单
map $http_user_agent $is_good_bot {
default 0;
~*googlebot 1;
~*bingbot 1;
~*slurp 1;
~*duckduckbot 1;
~*baiduspider 1;
~*yandexbot 1;
~*facebookexternalhit 1;
~*twitterbot 1;
~*linkedinbot 1;
}
# 可疑请求模式
map $request_uri $suspicious_request {
default 0;
~*\.(php|asp|aspx|jsp)$ 1;
~*\.(sql|bak|backup|old)$ 1;
~*/\.\./.*$ 1;
~*union.*select 1;
~*concat.*\( 1;
~*base64_decode 1;
~*script.*alert 1;
}
server {
listen 80;
server_name bot-protected.example.com;
# Bot访问控制
location / {
# 阻止恶意Bot(除非是好Bot)
if ($is_bot = 1) {
set $block_bot 1;
}
if ($is_good_bot = 1) {
set $block_bot 0;
}
if ($block_bot = 1) {
return 403 "Bot access denied";
}
# 阻止可疑请求
if ($suspicious_request = 1) {
return 403 "Suspicious request detected";
}
root /var/www/html;
try_files $uri $uri/ =404;
}
# Robots.txt
location = /robots.txt {
access_log off;
return 200 "User-agent: *\nDisallow: /admin/\nDisallow: /private/\nSitemap: https://example.com/sitemap.xml\n";
add_header Content-Type text/plain;
}
}
}
3.3 SQL注入和XSS防护
http {
# SQL注入检测
map $args $sql_injection {
default 0;
~*union.*select 1;
~*\bunion\b.*\bselect\b 1;
~*concat.*\( 1;
~*\bor\b.*\b1=1\b 1;
~*drop.*table 1;
~*insert.*into 1;
~*delete.*from 1;
~*update.*set 1;
~*\bexec\b.*\( 1;
~*script.*alert 1;
}
# XSS检测
map $args $xss_attack {
default 0;
~*<script 1;
~*javascript: 1;
~*onload= 1;
~*onerror= 1;
~*onclick= 1;
~*onmouseover= 1;
~*eval\( 1;
~*expression\( 1;
~*vbscript: 1;
}
# 文件包含攻击检测
map $args $file_inclusion {
default 0;
~*\.\./\.\. 1;
~*/etc/passwd 1;
~*/proc/self/environ 1;
~*php://filter 1;
~*php://input 1;
~*data: 1;
}
server {
listen 80;
server_name waf.example.com;
# Web应用防火墙规则
location / {
# SQL注入防护
if ($sql_injection = 1) {
access_log /var/log/nginx/security.log;
return 403 "SQL injection attempt detected";
}
# XSS防护
if ($xss_attack = 1) {
access_log /var/log/nginx/security.log;
return 403 "XSS attack attempt detected";
}
# 文件包含攻击防护
if ($file_inclusion = 1) {
access_log /var/log/nginx/security.log;
return 403 "File inclusion attempt detected";
}
# 检查请求体大小
client_max_body_size 10m;
root /var/www/html;
try_files $uri $uri/ =404;
}
# 特殊文件保护
location ~* \.(htaccess|htpasswd|ini|conf|bak|old|sql)$ {
deny all;
}
# 隐藏目录保护
location ~ /\. {
deny all;
}
}
}
4. 文件和目录安全
4.1 文件类型限制
server {
listen 80;
server_name file-secure.example.com;
# 禁止执行特定文件类型
location ~* \.(php|php3|php4|php5|phtml|pl|py|jsp|asp|aspx|cgi)$ {
# 只在特定目录允许PHP
if ($uri !~ ^/app/) {
return 403 "Execution not allowed";
}
# PHP处理(仅限/app/目录)
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 禁止访问备份和配置文件
location ~* \.(bak|backup|old|orig|tmp|temp|~|#|conf|ini|log)$ {
deny all;
}
# 源代码文件保护
location ~* \.(inc|class|lib|src)$ {
deny all;
}
# 上传目录安全
location /uploads/ {
# 禁止执行脚本
location ~ \.(php|php3|php4|php5|phtml|pl|py|jsp|asp|aspx|cgi)$ {
return 403 "Script execution not allowed in uploads directory";
}
# 只允许特定文件类型
location ~* \.(jpg|jpeg|png|gif|pdf|doc|docx|txt)$ {
expires 1d;
add_header Cache-Control "public";
}
# 其他文件类型拒绝访问
location ~* \.(.*)$ {
return 403 "File type not allowed";
}
}
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
}
4.2 目录浏览控制
server {
listen 80;
server_name directory-secure.example.com;
# 全局禁用目录浏览
autoindex off;
# 特定目录启用目录浏览(仅管理员)
location /files/ {
# IP访问控制
allow 192.168.1.0/24;
deny all;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
autoindex_format html;
}
# 私有目录保护
location /private/ {
# 基本认证
auth_basic "Private Area";
auth_basic_user_file /etc/nginx/.htpasswd;
# IP限制
allow 192.168.1.0/24;
deny all;
root /var/www;
try_files $uri $uri/ =404;
}
# 系统目录保护
location ~ ^/(bin|etc|var|usr|tmp|proc|sys)/ {
return 404;
}
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
}
5. 监控和日志安全
5.1 安全日志配置
http {
# 安全事件日志格式
log_format security '$remote_addr - [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$geoip_country_code" '
'"$args" "$request_body"';
# 错误日志格式
log_format error_detailed '$time_local [error] $pid#$tid: $errmsg '
'client: $remote_addr, server: $server_name, '
'request: "$request", host: "$host"';
server {
listen 80;
server_name monitored.example.com;
# 记录所有访问
access_log /var/log/nginx/security_access.log security;
# 记录安全事件
location / {
# 记录可疑活动
if ($args ~ "union|select|insert|delete|update|drop|exec|script") {
access_log /var/log/nginx/security_threats.log security;
}
# 记录失败的认证尝试
error_page 401 403 = @auth_failure;
root /var/www/html;
try_files $uri $uri/ =404;
}
location @auth_failure {
access_log /var/log/nginx/auth_failures.log security;
return 403 "Access denied";
}
}
}
5.2 实时安全监控
#!/bin/bash
# security-monitor.sh
SECURITY_LOG="/var/log/nginx/security_threats.log"
AUTH_LOG="/var/log/nginx/auth_failures.log"
ALERT_EMAIL="security@example.com"
TEMP_FILE="/tmp/security_alerts.tmp"
# 监控安全威胁
monitor_threats() {
local recent_threats=$(tail -100 $SECURITY_LOG | grep "$(date '+%d/%b/%Y:%H')" | wc -l)
if [ $recent_threats -gt 10 ]; then
echo "HIGH THREAT LEVEL: $recent_threats security events in the last hour" >> $TEMP_FILE
# 获取攻击者IP
tail -100 $SECURITY_LOG | grep "$(date '+%d/%b/%Y:%H')" | \
awk '{print $1}' | sort | uniq -c | sort -nr | head -5 >> $TEMP_FILE
fi
}
# 监控认证失败
monitor_auth_failures() {
local failed_attempts=$(tail -100 $AUTH_LOG | grep "$(date '+%d/%b/%Y:%H')" | wc -l)
if [ $failed_attempts -gt 20 ]; then
echo "HIGH AUTH FAILURE RATE: $failed_attempts failed attempts in the last hour" >> $TEMP_FILE
# 获取攻击来源
tail -100 $AUTH_LOG | grep "$(date '+%d/%b/%Y:%H')" | \
awk '{print $1}' | sort | uniq -c | sort -nr | head -5 >> $TEMP_FILE
fi
}
# 发送告警
send_alert() {
if [ -s $TEMP_FILE ]; then
{
echo "Security Alert - $(date)"
echo "================================"
cat $TEMP_FILE
echo ""
echo "Recent security events:"
tail -20 $SECURITY_LOG
} | mail -s "Security Alert - Nginx" $ALERT_EMAIL
rm -f $TEMP_FILE
fi
}
# 主监控循环
while true; do
> $TEMP_FILE
monitor_threats
monitor_auth_failures
send_alert
sleep 300 # 每5分钟检查一次
done
6. 自动防护脚本
6.1 自动IP封锁
#!/bin/bash
# auto-block.sh
LOG_FILE="/var/log/nginx/access.log"
BLOCK_LIST="/etc/nginx/conf.d/blocked_ips.conf"
THRESHOLD=100 # 每小时请求阈值
BAN_DURATION=3600 # 封锁时间(秒)
# 分析访问日志
analyze_log() {
local current_hour=$(date +"%d/%b/%Y:%H")
# 统计每个IP的请求数
grep "$current_hour" $LOG_FILE | \
awk '{print $1}' | \
sort | uniq -c | \
sort -nr | \
while read count ip; do
if [ $count -gt $THRESHOLD ]; then
echo "Blocking IP $ip (requests: $count)"
block_ip $ip
fi
done
}
# 封锁IP
block_ip() {
local ip=$1
local expire_time=$(($(date +%s) + $BAN_DURATION))
# 检查是否已经被封锁
if ! grep -q "deny $ip" $BLOCK_LIST; then
echo "# Blocked at $(date) - expires at $(date -d @$expire_time)" >> $BLOCK_LIST
echo "deny $ip;" >> $BLOCK_LIST
echo "# End block for $ip" >> $BLOCK_LIST
# 重新加载Nginx
nginx -s reload
# 记录封锁日志
echo "$(date): Blocked IP $ip for $BAN_DURATION seconds" >> /var/log/nginx/auto_block.log
fi
}
# 清理过期封锁
cleanup_expired() {
local temp_file="/tmp/blocked_ips.tmp"
local current_time=$(date +%s)
> $temp_file
while IFS= read -r line; do
if [[ $line =~ ^#.*expires\ at ]]; then
local expire_timestamp=$(echo $line | grep -o 'expires at .*' | cut -d' ' -f3-)
local expire_time=$(date -d "$expire_timestamp" +%s)
if [ $current_time -lt $expire_time ]; then
echo "$line" >> $temp_file
read -r next_line
echo "$next_line" >> $temp_file
read -r end_line
echo "$end_line" >> $temp_file
else
# 跳过过期的封锁条目
read -r next_line
read -r end_line
echo "$(date): Unblocked expired IP from $next_line" >> /var/log/nginx/auto_block.log
fi
else
echo "$line" >> $temp_file
fi
done < $BLOCK_LIST
mv $temp_file $BLOCK_LIST
nginx -s reload
}
# 主函数
main() {
# 创建封锁列表文件
if [ ! -f $BLOCK_LIST ]; then
echo "# Auto-generated blocked IPs" > $BLOCK_LIST
fi
while true; do
analyze_log
cleanup_expired
sleep 3600 # 每小时运行一次
done
}
main
6.2 安全配置验证
#!/bin/bash
# security-check.sh
CONFIG_FILE="/etc/nginx/nginx.conf"
SECURITY_SCORE=0
MAX_SCORE=100
check_server_tokens() {
if grep -q "server_tokens off" $CONFIG_FILE; then
echo "✓ Server tokens disabled"
SECURITY_SCORE=$((SECURITY_SCORE + 10))
else
echo "✗ Server tokens not disabled"
fi
}
check_ssl_config() {
if grep -q "ssl_protocols.*TLSv1\.3" $CONFIG_FILE; then
echo "✓ TLS 1.3 enabled"
SECURITY_SCORE=$((SECURITY_SCORE + 15))
else
echo "✗ TLS 1.3 not enabled"
fi
if grep -q "ssl_session_tickets off" $CONFIG_FILE; then
echo "✓ SSL session tickets disabled"
SECURITY_SCORE=$((SECURITY_SCORE + 5))
else
echo "✗ SSL session tickets not disabled"
fi
}
check_security_headers() {
local headers=("X-Frame-Options" "X-Content-Type-Options" "X-XSS-Protection" "Strict-Transport-Security")
for header in "${headers[@]}"; do
if grep -q "$header" /etc/nginx/sites-available/*; then
echo "✓ $header configured"
SECURITY_SCORE=$((SECURITY_SCORE + 5))
else
echo "✗ $header not configured"
fi
done
}
check_rate_limiting() {
if grep -q "limit_req_zone" $CONFIG_FILE; then
echo "✓ Rate limiting configured"
SECURITY_SCORE=$((SECURITY_SCORE + 15))
else
echo "✗ Rate limiting not configured"
fi
}
check_access_control() {
if grep -q "allow\|deny" /etc/nginx/sites-available/*; then
echo "✓ Access control configured"
SECURITY_SCORE=$((SECURITY_SCORE + 10))
else
echo "✗ Access control not configured"
fi
}
check_file_permissions() {
local config_perms=$(stat -c "%a" $CONFIG_FILE)
if [ "$config_perms" = "644" ] || [ "$config_perms" = "640" ]; then
echo "✓ Nginx config file permissions are secure"
SECURITY_SCORE=$((SECURITY_SCORE + 5))
else
echo "✗ Nginx config file permissions are not secure ($config_perms)"
fi
}
generate_report() {
echo ""
echo "==================================="
echo "Nginx Security Assessment Report"
echo "==================================="
echo "Security Score: $SECURITY_SCORE/$MAX_SCORE"
echo ""
if [ $SECURITY_SCORE -ge 80 ]; then
echo "Security Level: GOOD"
elif [ $SECURITY_SCORE -ge 60 ]; then
echo "Security Level: MODERATE"
else
echo "Security Level: POOR"
fi
echo ""
echo "Recommendations:"
if [ $SECURITY_SCORE -lt $MAX_SCORE ]; then
echo "- Review and implement missing security configurations"
echo "- Regularly update Nginx and system packages"
echo "- Monitor security logs for suspicious activities"
echo "- Consider implementing Web Application Firewall (WAF)"
fi
}
# 运行检查
echo "Running Nginx Security Check..."
echo ""
check_server_tokens
check_ssl_config
check_security_headers
check_rate_limiting
check_access_control
check_file_permissions
generate_report
小结
通过本文的学习,你应该掌握:
- 基础安全配置和信息隐藏技巧
- 安全头部和内容安全策略配置
- 访问控制和地理位置限制
- DDoS防护和Bot防护措施
- SQL注入和XSS攻击防护
- 文件和目录安全控制
- 安全监控和自动防护脚本
- 安全配置的验证和评估
下一篇文章将介绍访问控制与限流的详细配置。