一、核心安全配置
1. 隐藏版本号
隐藏Nginx版本号是安全加固的第一步,可以防止攻击者针对特定版本漏洞进行攻击。
nginx
# 1. 修改nginx.conf全局配置
http {
# 隐藏版本号
server_tokens off;
# 自定义错误页面(可选,避免泄露信息)
error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 500 501 502 503 504 505 /error.html;
# 或者在不同server块中设置
server {
listen 80;
server_name example.com;
# 关闭版本号显示
server_tokens off;
# 自定义错误页面
error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/html;
internal;
}
}
}
# 2. 修改源码彻底隐藏(需要编译)
# 在编译前修改 src/core/nginx.h 文件
# vi src/core/nginx.h
# 修改以下行:
#define NGINX_VERSION "2.4.6"
#define NGINX_VER "Apache/" NGINX_VERSION
# 或修改为自定义字符串
#define NGINX_VER "Microsoft-IIS/10.0"
# 3. 修改fastcgi参数(防止PHP泄露)
location ~ \.php$ {
fastcgi_param SERVER_SOFTWARE "Microsoft-IIS/10.0";
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
}
# 4. 验证配置
curl -I http://example.com
# 响应头中不应包含 Server: nginx/版本号
# 应为 Server: nginx 或自定义名称
2. 限制危险请求方法
HTTP协议定义了多种请求方法,其中一些可能存在安全风险,需要限制使用。
nginx
# 1. 全局限制危险请求方法
server {
listen 80;
server_name example.com;
# 只允许GET、HEAD、POST方法
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 405;
}
# 更精细的控制
location / {
limit_except GET HEAD POST {
deny all;
}
}
# 2. 针对API接口的限制
location /api/ {
# API只允许GET和POST
limit_except GET POST {
deny all;
}
# 禁止TRACE方法
if ($request_method = TRACE) {
return 405;
}
proxy_pass http://backend;
}
# 3. 禁止特定方法
location / {
# 拒绝DELETE、PUT等方法
if ($request_method ~ ^(DELETE|PUT|PATCH|TRACE|CONNECT|OPTIONS)$) {
return 405;
}
}
# 4. 只允许GET的静态资源
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
# 静态资源只允许GET和HEAD
limit_except GET HEAD {
deny all;
}
expires 30d;
access_log off;
}
# 5. OPTIONS方法处理(CORS预检请求)
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}
}
3. 请求限制(CC攻击防御)
3.1 连接数限制
nginx
# 1. 定义限制区域
http {
# 限制每个IP的连接数
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# 限制每个IP的请求速率
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# 动态黑名单(结合geo模块)
geo $blacklist {
default 0;
192.168.1.100 1; # 手动加入黑名单的IP
10.0.0.0/24 1;
}
server {
listen 80;
server_name example.com;
# 2. 应用连接数限制
location / {
# 每个IP最多同时10个连接
limit_conn conn_limit 10;
# 请求速率限制:平均10r/s,突发20
limit_req zone=req_limit burst=20 nodelay;
# 返回状态码
limit_conn_status 503;
limit_req_status 503;
# 日志级别
limit_conn_log_level warn;
limit_req_log_level warn;
proxy_pass http://backend;
}
# 3. 针对不同URL设置不同限制
location /api/ {
# API接口限制更严格
limit_req zone=req_limit burst=5 nodelay;
limit_conn conn_limit 5;
proxy_pass http://api_backend;
}
location /login/ {
# 登录接口限制(防止暴力破解)
limit_req zone=req_limit burst=3 nodelay;
limit_conn conn_limit 2;
proxy_pass http://login_backend;
}
location /static/ {
# 静态资源不限制
limit_req off;
limit_conn off;
expires 30d;
}
}
}
3.2 带宽限制
nginx
# 1. 限制下载速度
http {
# 定义限制区域
limit_rate_zone $binary_remote_addr zone=rate_limit:10m;
server {
listen 80;
# 2. 全局速度限制
location /download/ {
# 每个连接限速200KB/s
limit_rate 200k;
# 超过1MB后开始限速
limit_rate_after 1m;
root /data/downloads;
autoindex on;
}
# 3. 不同用户不同速度
location /files/ {
if ($http_user_agent ~* "mobile") {
set $limit_rate 100k; # 移动用户限速100KB/s
}
if ($http_user_agent ~* "bot") {
set $limit_rate 50k; # 爬虫限速50KB/s
}
limit_rate $limit_rate;
root /data/files;
}
# 4. 大文件下载限速
location /bigfile/ {
alias /data/bigfiles/;
# 限制总带宽
limit_conn conn_limit 5;
limit_rate 500k;
# 大文件分段下载支持
add_header Accept-Ranges bytes;
}
}
}
3.3 CC攻击防御综合配置
nginx
# /etc/nginx/conf.d/cc_defense.conf
http {
# 1. 定义多个限制区域
# 基于IP的请求限制
limit_req_zone $binary_remote_addr zone=per_ip:10m rate=10r/s;
# 基于User-Agent的限制
limit_req_zone $http_user_agent zone=agent:10m rate=20r/s;
# 基于URI的限制
limit_req_zone $request_uri zone=uri:10m rate=30r/s;
# 连接数限制
limit_conn_zone $binary_remote_addr zone=addr:10m;
# 2. 动态黑名单
# 使用map定义黑名单条件
map $remote_addr $limit_ip {
default 1;
192.168.1.0/24 0; # 内网不限制
10.0.0.0/8 0;
}
server {
listen 80;
server_name example.com;
# 3. 综合限制
location / {
# IP请求限制
limit_req zone=per_ip burst=20 delay=5;
# 连接数限制
limit_conn addr 20;
# 异常请求判断
if ($http_user_agent ~* (bot|spider|crawler)) {
set $bot_limit 1;
}
# 高频访问IP记录
access_log /var/log/nginx/access.log combined;
proxy_pass http://backend;
}
# 4. 验证码验证(需要第三方模块)
location /captcha {
captcha on;
captcha_secret your_secret_key;
captcha_mode image;
if ($http_cookie !~* "captcha_pass") {
return 302 /captcha?return=$request_uri;
}
}
# 5. JS挑战(防CC)
location /challenge {
js_challenge on;
js_challenge_secret your_secret;
if ($http_cookie !~* "js_pass") {
return 302 /challenge?return=$request_uri;
}
}
# 6. 记录攻击IP
location @blocked {
access_log /var/log/nginx/blocked.log combined;
return 503;
}
# 7. 封禁IP列表
include /etc/nginx/blockips.conf;
}
}
3.4 动态黑名单配置
bash
#!/bin/bash
# dynamic_blacklist.sh - 动态黑名单管理脚本
# 配置
BLOCK_LOG="/var/log/nginx/blocked.log"
BLOCK_CONF="/etc/nginx/blockips.conf"
NGINX_CONF="/etc/nginx/nginx.conf"
THRESHOLD=100 # 阈值:100次请求
INTERVAL=300 # 时间窗口:5分钟
# 分析日志,找出攻击IP
analyze_log() {
local time_ago=$(date -d "$INTERVAL seconds ago" +"%Y-%m-%d %H:%M:%S")
# 找出在时间窗口内请求次数超过阈值的IP
awk -v time="$time_ago" -v threshold="$THRESHOLD" '
$4 >= time {count[$1]++}
END {
for (ip in count) {
if (count[ip] > threshold) {
print ip, count[ip]
}
}
}
' $BLOCK_LOG | sort -k2 -nr
}
# 生成封禁配置
generate_block_conf() {
local tmp_conf="${BLOCK_CONF}.tmp"
# 写入头部
cat > $tmp_conf << EOF
# 自动生成的IP黑名单
# 更新时间: $(date)
EOF
# 添加封禁规则
while read ip count; do
echo "deny $ip; # 请求数: $count" >> $tmp_conf
done < <(analyze_log)
# 比较变化
if [ -f $BLOCK_CONF ]; then
if ! cmp -s $BLOCK_CONF $tmp_conf; then
mv $tmp_conf $BLOCK_CONF
return 0 # 有变化
fi
else
mv $tmp_conf $BLOCK_CONF
return 0
fi
rm -f $tmp_conf
return 1 # 无变化
}
# 重载Nginx
reload_nginx() {
nginx -t && systemctl reload nginx
echo "$(date): 黑名单已更新,Nginx重载完成" >> /var/log/blacklist.log
}
# 主函数
main() {
if generate_block_conf; then
reload_nginx
else
echo "$(date): 黑名单无变化" >> /var/log/blacklist.log
fi
}
# 定时任务
# crontab -e
# */5 * * * * /bin/bash /usr/local/bin/dynamic_blacklist.sh
main
二、高级防护
1. 防止DDoS攻击
nginx
# /etc/nginx/conf.d/ddos_protection.conf
http {
# 1. SYN Flood防护(需要内核配合)
# sysctl -w net.ipv4.tcp_syncookies=1
# 2. 连接限制
limit_conn_zone $binary_remote_addr zone=ddos_conn:10m;
limit_req_zone $binary_remote_addr zone=ddos_req:10m rate=50r/s;
# 3. 缓冲区大小限制
client_body_buffer_size 8k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
client_max_body_size 10m;
# 4. 超时设置
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 10s;
send_timeout 10s;
server {
listen 80;
server_name example.com;
# 5. DDoS防护配置
location / {
# 连接数限制
limit_conn ddos_conn 10;
# 请求速率限制
limit_req zone=ddos_req burst=100 nodelay;
# 限制特定User-Agent
if ($http_user_agent ~* (slowhttptest|python|perl|ruby)) {
return 403;
}
# 限制空User-Agent
if ($http_user_agent = "") {
return 403;
}
# 限制特定Referer
if ($http_referer ~* (spam|bot|crawler)) {
return 403;
}
proxy_pass http://backend;
}
# 6. 验证码保护
location /captcha/ {
# 需要安装第三方模块
captcha on;
captcha_secret your_secret;
captcha_mode image;
captcha_expire 300;
}
}
}
2. 防止慢速攻击
nginx
# /etc/nginx/conf.d/slow_attack.conf
http {
# 1. 限制请求头大小
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# 2. 限制请求体大小
client_body_buffer_size 8k;
client_max_body_size 10m;
# 3. 超时设置(防止慢速连接)
client_body_timeout 10s; # 读取请求体超时
client_header_timeout 10s; # 读取请求头超时
keepalive_timeout 10s; # keepalive超时
send_timeout 10s; # 发送响应超时
# 4. 限制每个连接的数据量
limit_rate 100k; # 每个连接限速100KB/s
server {
listen 80;
# 5. 针对慢速攻击的防护
location / {
# 如果请求头太大,直接拒绝
if ($http_content_length > 10485760) {
return 413;
}
# 限制请求时间
fastcgi_read_timeout 30s;
proxy_read_timeout 30s;
proxy_pass http://backend;
}
# 6. 监控慢速连接
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
}
3. 防止HTTP走私攻击
nginx
# /etc/nginx/conf.d/smuggling_protection.conf
http {
# 1. 清理请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 2. 规范化请求
# 删除重复的Transfer-Encoding头
set $transfer_encoding $http_transfer_encoding;
if ($http_transfer_encoding ~* "chunked") {
set $transfer_encoding "";
}
# 3. 限制HTTP版本
if ($server_protocol !~* "HTTP/1\.(0|1)" ) {
return 405;
}
server {
listen 80;
# 4. 检查Content-Length
location / {
# 如果同时存在Transfer-Encoding和Content-Length
if ($http_transfer_encoding ~* "chunked") {
if ($http_content_length) {
return 400;
}
}
# 检查Content-Length格式
if ($http_content_length !~ "^[0-9]+$") {
return 400;
}
proxy_pass http://backend;
}
}
}
4. 防止DNS Rebinding攻击
nginx
# /etc/nginx/conf.d/dns_rebinding.conf
http {
# 1. 验证Host头
server {
listen 80;
server_name example.com www.example.com;
# 2. 拒绝无效Host
if ($host !~* ^(example\.com|www\.example\.com)$) {
return 444;
}
# 3. 检查Host头长度
if ($http_host) {
if ($http_host ~ "^.{255,}") {
return 400;
}
}
# 4. 防止IP直接访问
server {
listen 80 default_server;
server_name _;
# 如果是IP访问,返回444
if ($host ~* "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$") {
return 444;
}
return 444;
}
}
}
三、Nginx HTTPS配置
1. HTTPS概念
HTTPS(Hypertext Transfer Protocol Secure) 是HTTP的安全版本,通过SSL/TLS协议提供加密通信。
text
HTTP (80) → 明文传输
HTTPS (443) → 加密传输
2. HTTP为什么不安全
HTTP协议存在以下安全问题:
text
1. 明文传输
├── 数据可被窃听(抓包)
├── 数据可被篡改(中间人攻击)
└── 身份可被伪造(钓鱼网站)
2. 无身份验证
├── 无法确认服务器身份
└── 无法确认客户端身份
3. 无完整性校验
├── 无法检测数据是否被修改
└── 无法防止重放攻击
3. 安全通信的四大原则
text
1. 机密性(Confidentiality)
├── 数据加密,防止窃听
└── 使用对称加密算法(AES、ChaCha20)
2. 完整性(Integrity)
├── 数据不可篡改
└── 使用消息认证码(MAC)
3. 身份验证(Authentication)
├── 验证通信双方身份
└── 使用数字证书(CA签发)
4. 不可否认性(Non-repudiation)
├── 无法否认已发送的数据
└── 使用数字签名
4. HTTPS通信原理简述
text
HTTPS握手过程:
1. TCP三次握手
Client → Server: SYN
Server → Client: SYN+ACK
Client → Server: ACK
2. TLS握手(以TLS 1.3为例)
Client → Server: ClientHello (支持的加密套件、随机数)
Server → Client: ServerHello (选择的加密套件、随机数)
Server → Client: Certificate (服务器证书)
Server → Client: ServerFinished (握手完成)
3. 密钥交换
Client → Server: ClientFinished (加密的握手消息)
Server → Client: 应用数据 (加密传输)
4. 加密通信
Client ↔ Server: 加密的应用数据
5. HTTPS证书准备
bash
# 1. 自签名证书(测试用)
# 生成私钥
openssl genrsa -out example.com.key 2048
# 生成CSR(证书签名请求)
openssl req -new -key example.com.key -out example.com.csr
# 填写信息:
# Country Name: CN
# State: Beijing
# Locality: Beijing
# Organization Name: Example Inc
# Common Name: example.com
# 生成自签名证书
openssl x509 -req -days 365 -in example.com.csr -signkey example.com.key -out example.com.crt
# 2. 生成带SAN的自签名证书
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout example.com.key \
-out example.com.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Example/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com"
# 3. Let's Encrypt免费证书(推荐)
# 安装certbot
# CentOS
yum install certbot python3-certbot-nginx
# Ubuntu
apt install certbot python3-certbot-nginx
# 获取证书
certbot --nginx -d example.com -d www.example.com
# 自动续期
certbot renew --dry-run
# 4. 查看证书信息
openssl x509 -in example.com.crt -text -noout
# 5. 检查证书有效期
openssl x509 -in example.com.crt -noout -enddate
6. 基础HTTPS配置
nginx
# /etc/nginx/conf.d/example.com-ssl.conf
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# 证书路径
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# 网站根目录
root /var/www/example.com;
index index.html index.php;
# 访问日志
access_log /var/log/nginx/example.com-ssl_access.log main;
error_log /var/log/nginx/example.com-ssl_error.log warn;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
}
# HTTP重定向到HTTPS
server {
listen 80;
server_name example.com www.example.com;
# 永久重定向
return 301 https://$server_name$request_uri;
# 或者保留请求方法的重定向
# return 308 https://$server_name$request_uri;
}
7. 强化HTTPS配置
nginx
# /etc/nginx/conf.d/ssl_hardening.conf
server {
listen 443 ssl http2;
server_name example.com;
# 证书配置
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_trusted_certificate /etc/nginx/ssl/chain.crt;
# 1. SSL协议和加密套件
# 禁用旧版协议,只启用TLS 1.2和1.3
ssl_protocols TLSv1.2 TLSv1.3;
# 加密套件(推荐配置)
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_prefer_server_ciphers on;
# 2. DH参数(提高安全性)
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# 生成DH参数
# openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
# 3. 会话缓存
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# 4. OCSP Stapling(提高性能和安全)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 5. HSTS(强制HTTPS)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 6. 安全响应头
# 防止XSS攻击
add_header X-XSS-Protection "1; mode=block" always;
# 防止MIME类型嗅探
add_header X-Content-Type-Options "nosniff" always;
# 点击劫持防护
add_header X-Frame-Options "SAMEORIGIN" always;
# 内容安全策略
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https:;" always;
# 限制引用策略
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 7. 特征移除
# 隐藏Nginx版本
server_tokens off;
# 移除X-Powered-By
proxy_hide_header X-Powered-By;
fastcgi_hide_header X-Powered-By;
# 8. 证书透明度
# 如果需要,可以添加SCT(Signed Certificate Timestamps)
# ssl_ct on;
location / {
proxy_pass http://backend;
include /etc/nginx/proxy_params;
}
}
8. HTTPS性能优化
nginx
# /etc/nginx/conf.d/ssl_performance.conf
http {
# 1. SSL会话复用
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 4h;
# 2. SSL缓冲区大小
ssl_buffer_size 4k;
# 3. 启用HTTP/2
server {
listen 443 ssl http2;
server_name example.com;
# HTTP/2优化
http2_max_concurrent_streams 128;
http2_idle_timeout 300s;
# 4. OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# 5. 减少TLS握手
ssl_session_tickets on;
# 6. 启用压缩(注意:压缩可能带来安全风险,谨慎使用)
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
# 7. 静态资源优化
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
add_header Cache-Control "public, immutable";
access_log off;
# 开启sendfile
sendfile on;
tcp_nopush on;
}
# 8. 代理优化
location / {
proxy_pass http://backend;
# 启用缓存
proxy_cache my_cache;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 1m;
# 减少数据拷贝
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 8k;
# 保持连接
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
9. 多域名HTTPS配置
nginx
# /etc/nginx/conf.d/multi-domain.conf
# 1. 多证书配置(传统方式)
server {
listen 443 ssl http2;
server_name domain1.com www.domain1.com;
ssl_certificate /etc/nginx/ssl/domain1.com.crt;
ssl_certificate_key /etc/nginx/ssl/domain1.com.key;
root /var/www/domain1;
# ... 其他配置
}
server {
listen 443 ssl http2;
server_name domain2.com www.domain2.com;
ssl_certificate /etc/nginx/ssl/domain2.com.crt;
ssl_certificate_key /etc/nginx/ssl/domain2.com.key;
root /var/www/domain2;
# ... 其他配置
}
# 2. SNI(Server Name Indication)配置
# Nginx自动支持SNI,可以一个IP多个证书
# 3. 通配符证书
server {
listen 443 ssl http2;
server_name *.example.com;
ssl_certificate /etc/nginx/ssl/wildcard.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/wildcard.example.com.key;
# 根据域名分发到不同后端
location / {
if ($host ~* ^api\.example\.com$) {
proxy_pass http://api_backend;
break;
}
if ($host ~* ^admin\.example\.com$) {
proxy_pass http://admin_backend;
break;
}
proxy_pass http://default_backend;
}
}
10. HTTPS监控和调试
bash
#!/bin/bash
# ssl_monitor.sh - SSL证书监控脚本
# 配置
DOMAINS="example.com www.example.com api.example.com"
EMAIL="admin@example.com"
WARN_DAYS=30
SSL_DIR="/etc/nginx/ssl"
# 检查证书过期
check_expiry() {
local domain=$1
local cert_file="${SSL_DIR}/${domain}.crt"
if [ ! -f "$cert_file" ]; then
echo "证书文件不存在: $cert_file"
return 1
fi
# 获取过期时间
expiry_date=$(openssl x509 -in "$cert_file" -noout -enddate | cut -d= -f2)
expiry_timestamp=$(date -d "$expiry_date" +%s)
current_timestamp=$(date +%s)
days_left=$(( ($expiry_timestamp - $current_timestamp) / 86400 ))
echo "域名: $domain"
echo "过期时间: $expiry_date"
echo "剩余天数: $days_left"
if [ $days_left -lt $WARN_DAYS ]; then
echo "警告: 证书即将过期!"
return 1
elif [ $days_left -lt 0 ]; then
echo "错误: 证书已过期!"
return 2
fi
return 0
}
# 检查证书链
check_chain() {
local domain=$1
local cert_file="${SSL_DIR}/${domain}.crt"
echo "验证证书链..."
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt "$cert_file"
}
# 检查OCSP
check_ocsp() {
local domain=$1
echo "检查OCSP Stapling..."
echo QUIT | openssl s_client -connect ${domain}:443 -status 2>/dev/null | grep -A 20 "OCSP response"
}
# SSL Labs测试(需要网络)
test_ssllabs() {
local domain=$1
echo "SSL Labs评分: https://www.ssllabs.com/ssltest/analyze.html?d=$domain"
}
# 发送告警
send_alert() {
local subject="SSL证书监控告警"
local message="$1"
echo "$message" | mail -s "$subject" "$EMAIL"
}
# 主函数
main() {
for domain in $DOMAINS; do
echo "================================="
echo "检查域名: $domain"
echo "================================="
check_expiry $domain
if [ $? -ne 0 ]; then
send_alert "域名 $domain 证书即将过期,请及时更新"
fi
check_chain $domain
check_ocsp $domain
echo ""
done
}
# 定时检查
# crontab -e
# 0 9 * * * /bin/bash /usr/local/bin/ssl_monitor.sh
main
11. 自动化证书更新
bash
#!/bin/bash
# auto_renew_ssl.sh - 自动更新Let's Encrypt证书
# 配置
DOMAINS="example.com www.example.com"
EMAIL="admin@example.com"
WEBROOT="/var/www/html"
LE_PATH="/usr/bin/certbot"
NGINX_SERVICE="nginx"
# 更新证书
renew_cert() {
$LE_PATH certonly --webroot -w $WEBROOT \
--email $EMAIL \
--agree-tos \
--no-eff-email \
--force-renewal \
-d ${DOMAINS// / -d }
if [ $? -eq 0 ]; then
echo "$(date): 证书更新成功" >> /var/log/ssl-renew.log
# 重载Nginx
systemctl reload $NGINX_SERVICE
# 发送成功通知
echo "SSL证书已更新" | mail -s "SSL Renewal Success" $EMAIL
else
echo "$(date): 证书更新失败" >> /var/log/ssl-renew.log
# 发送失败通知
echo "SSL证书更新失败,请手动检查" | mail -s "SSL Renewal Failed" $EMAIL
fi
}
# 检查是否需要更新
check_needs_renewal() {
for domain in $DOMAINS; do
cert_file="/etc/letsencrypt/live/$domain/fullchain.pem"
if [ -f "$cert_file" ]; then
# 如果证书将在30天内过期,则更新
if ! openssl x509 -in "$cert_file" -checkend 2592000 > /dev/null; then
return 0 # 需要更新
fi
else
return 0 # 证书不存在
fi
done
return 1 # 不需要更新
}
# 主函数
main() {
if check_needs_renewal; then
renew_cert
else
echo "$(date): 证书仍在有效期内,无需更新" >> /var/log/ssl-renew.log
fi
}
# 每天检查一次
# crontab -e
# 0 3 * * * /bin/bash /usr/local/bin/auto_renew_ssl.sh
main
12. HTTPS常见问题排查
bash
# 1. 检查证书
openssl x509 -in /etc/nginx/ssl/example.com.crt -text -noout
# 2. 检查私钥
openssl rsa -in /etc/nginx/ssl/example.com.key -check
# 3. 检查证书和私钥是否匹配
openssl x509 -noout -modulus -in example.com.crt | openssl md5
openssl rsa -noout -modulus -in example.com.key | openssl md5
# 两个输出应该相同
# 4. 测试SSL连接
openssl s_client -connect example.com:443 -servername example.com
# 5. 测试TLS 1.2
openssl s_client -connect example.com:443 -tls1_2
# 6. 测试TLS 1.3
openssl s_client -connect example.com:443 -tls1_3
# 7. 检查证书链
openssl s_client -connect example.com:443 -showcerts
# 8. 检查OCSP
openssl s_client -connect example.com:443 -status
# 9. 使用curl测试
curl -vI https://example.com
curl --http2 -I https://example.com
# 10. SSL Labs测试
# 访问 https://www.ssllabs.com/ssltest/analyze.html?d=example.com
# 11. 检查配置错误
nginx -t
# 12. 查看错误日志
tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log | grep SSL
四、综合安全配置示例
nginx
# /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 {
use epoll;
worker_connections 10240;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 隐藏版本号
server_tokens off;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format security '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
# 基础优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# 客户端设置
client_max_body_size 20m;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# 超时设置
client_body_timeout 10s;
client_header_timeout 10s;
send_timeout 10s;
# 限制区域
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# 代理设置
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffer_size 4k;
proxy_buffers 8 8k;
proxy_busy_buffers_size 16k;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
# SSL配置
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
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';
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 包含站点配置
include /etc/nginx/conf.d/*.conf;
}