预计阅读时间:15 分钟 | 难度:进阶 ⭐⭐⭐
很多开发者觉得"我的网站又没用户,没人会攻击的"——事实是:互联网上存在大量的自动化扫描脚本,它们无差别地扫描每个公网 IP。你的服务器在连上网的第一小时内就会收到 SSH 爆破尝试。
安全不是"等网站做大了再考虑"的事,而是从部署第一天起就要做的事。这节课会把之前各课提到的安全要点汇总,并深入覆盖你没注意到的关键领域。
很多人的误区是只配了云安全组或只配了 UFW。正确做法是两者都配——云安全组做第一道防线,服务器内部防火墙做第二道。
云安全组建议规则(以阿里云为例):
# 阿里云安全组规则配置建议
# 入站规则(Inbound)
协议: TCP, 端口: 22, 源: 你的家庭/公司 IP(或 VPN IP)# SSH 不要对全世界开放
协议: TCP, 端口: 80, 源: 0.0.0.0/0 # HTTP
协议: TCP, 端口: 443, 源: 0.0.0.0/0 # HTTPS
# 如果不需要直接 SSH,可用阿里云 ECS 的"会话管理"代替 SSH
服务器内部 UFW 配置:
# 默认策略:拒绝所有入站,允许所有出站
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 只放行必要的端口
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
# 启用防火墙
sudo ufw enable
sudo ufw status verbose # 查看完整规则
即使你禁用了密码登录,SSH 端口仍然会被扫描。Fail2ban 监控日志文件,检测到多次失败尝试后自动封禁 IP:
sudo apt install -y fail2ban
# 创建本地配置文件(不要直接改 /etc/fail2ban/jail.conf)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# 编辑 jail.local,找到 [sshd] 部分
# 确保以下设置:
[sshd]
enabled = true
maxretry = 5 # 5 次失败后封禁
bantime = 3600 # 封禁 1 小时(默认 600 秒)
findtime = 600 # 10 分钟内的失败计数
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd # 查看封禁状态
配置了 Certbot 后,用户如果输入 http:// 仍然会被重定向到 HTTPS。但更好的做法是告诉浏览器"以后只走 HTTPS":
# 在 Nginx 的 server block 中添加(certbot 配置的 443 区块里)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# max-age=63072000 秒(2 年)
# includeSubDomains 对所有子域名生效
# ⚠️ 确认所有子域名都支持 HTTPS 后再加 includeSubDomains
# /etc/nginx/conf.d/security-headers.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
这些头信息帮助浏览器防范点击劫持、MIME 嗅探等常见攻击。
证书到期后网站会直接崩掉。设置一个简单的定时检查脚本:
# 写一个简单的证书到期检查脚本
cat > ~/check-cert.sh << 'EOF'
#!/bin/bash
DOMAIN="example.com"
EXPIRY=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "Certificate for $DOMAIN expires in $DAYS_LEFT days"
if [ $DAYS_LEFT -lt 7 ]; then echo "WARNING: Certificate expiring soon!"; fi
EOF
chmod +x ~/check-cert.sh
./check-cert.sh
数据库是网站的核心资产——用户数据、订单、内容都在里面。数据库被攻破 = 灾难性的数据泄露。
/etc/mysql/mysql.conf.d/mysqld.cnf 中设置 bind-address = 127.0.0.1,禁止远程连接sudo mysql -e "DROP DATABASE IF EXISTS test;"# MySQL 安全初始化(生产环境必须跑)
sudo mysql_secure_installation
# 创建专用用户(示例)
CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'your-strong-password-here';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'myapp'@'localhost';
FLUSH PRIVILEGES;
ssh -L 3306:127.0.0.1:3306 ubuntu@你的服务器
"我的数据不重要的,丢了也没关系"——几乎所有说过这句话的人最后都后悔了。
# /usr/local/bin/backup-db.sh
#!/bin/bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/mysql"
DB_NAME="myapp_db"
DB_USER="backup_user"
DB_PASSWORD="your-backup-password"
RETENTION_DAYS=7
mkdir -p $BACKUP_DIR
# 使用 mysqldump 备份
mysqldump -u $DB_USER -p$DB_PASSWORD $DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${TIMESTAMP}.sql.gz
# 删除 7 天前的旧备份
find $BACKUP_DIR -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: ${DB_NAME}_${TIMESTAMP}.sql.gz"
# 添加到 crontab 每天凌晨 3 点执行
sudo crontab -e
# 添加这一行
0 3 * * * /usr/local/bin/backup-db.sh
仅在服务器上的备份不算真正的备份——服务器硬盘坏了或整台机器被攻击了,备份也跟着没了。你需要异地备份。
# 使用 aws-cli 将备份同步到 S3 / 阿里云 OSS
# (需要先安装和配置 aws-cli 或 ossutil)
aws s3 sync /backups/ s3://myapp-backups/production/
# 或者使用 rclone(支持几乎所有云存储)
rclone sync /backups/ remote:myapp-backups
日志文件会无限增长,占满磁盘空间。Linux 的 logrotate 自动按天/大小轮转日志:
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily # 每天轮转
rotate 30 # 保留 30 天的日志
compress # 轮转后压缩
delaycompress
missingok
notifempty
create 0640 ubuntu ubuntu
sharedscripts
postrotate
systemctl reload myapp
endscript
}
| 日志文件 | 查看什么 |
|---|---|
/var/log/nginx/access.log | Nginx 访问日志——谁在什么时间访问了什么路径 |
/var/log/nginx/error.log | Nginx 错误日志——502/504 等错误的线索 |
/var/log/auth.log | SSH 登录日志——有没有人尝试登录失败 |
/var/log/syslog | 系统通用日志 |
journalctl -u myapp | 应用(Systemd 服务)日志 |
journalctl -u docker | Docker 守护进程日志 |
不需要复杂的 Prometheus + Grafana 体系。对于单台或几台服务器,最简单有效的监控方案是:
Netdata 是一个开源的实时监控工具,一条命令安装完就能看到 CPU、内存、磁盘、网络、进程等所有指标:
# 一条命令安装 Netdata
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
# 安装完成后访问 http://你的服务器:19999
# ⚠️ 记得在安全组中限制 19999 端口只能由你的 IP 访问
# 一个简单的磁盘告警脚本
#!/bin/bash
THRESHOLD=80
USAGE=$(df -h / | awk 'NR==2 {print $5}' | cut -d% -f1)
if [ $USAGE -gt $THRESHOLD ]; then
echo "Disk usage is at ${USAGE}% on $(hostname)" | mail -s "Disk Alert" your-email@example.com
fi
从外部监控你的网站是否在线(内部监控看不出来网络层面或 DNS 问题):
当最坏的情况发生时(服务器被入侵、数据被删除、硬盘故障),你需要一个文档化的恢复流程,而不是临时想该怎么办。
你能做的最好准备:
✅ 生产环境上线前逐项检查:
Q1: 一个安全的数据库应该绑定到哪个地址?
Q2: 以下哪个是正确的备份策略?
Q3: 你的网站仅支持 HTTPS。用户通过 http:// 访问时应如何正确配置?
恭喜你完成了全部 5 节课!🎉
你走过的学习路径:
接下来的实践建议: