本文约 6,095 字,预计阅读 16 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:公网服务器存活时间以分钟计
一台新开的云服务器,只要公网 IP 一生效,来自全球的自动化扫描就开始敲门了。这不是夸张——把 SSH 开在 22 端口、密码强度一般的机器,通常在几小时内就会出现在爆破日志里,运气不好几天内就会被植入挖矿程序。
WordPress 站点因为市场份额高,是扫描器的重点关照对象:/wp-login.php 的暴力破解、/xmlrpc.php 的 pingback 放大攻击、插件目录的已知漏洞探测,几乎每分钟都在发生。很多站长觉得”我就是个小博客,没人会攻击我”——实际上自动化扫描不挑目标,它只挑漏洞。上线前的这几项基线配置总共花不到一小时,却能挡掉 95% 以上的自动化攻击。
原理分析:攻击面来自哪里
服务器的安全风险可以拆成四层:
第一层:远程登录入口(SSH)。 默认 22 端口 + root 用户 + 密码登录,是最经典的组合风险。攻击者用密码字典不断尝试,一旦命中就是最高权限。
第二层:端口暴露面。 每一台服务器默认可能开放 22(SSH)、3306(MySQL)、6379(Redis)、80/443(Web)等端口。其中 3306 和 6379 绝对不应该对公网开放——Redis 未授权访问是最常见的一键沦陷方式,攻击者可以直接写入 SSH 公钥或 crontab。
第三层:Web 应用层。 WordPress 的攻击面集中在登录页、XML-RPC、插件与主题漏洞、以及文件权限。其中 xmlrpc.php 的 system.multicall 方法可以在一次请求里尝试上千个密码,绕过常规的登录失败次数限制。
第四层:数据可恢复性。 前面三层都被突破的情况下(勒索、误操作、云厂商故障),唯一能救你的是备份。而”备份在同一台机器上”等于没备份。
防护优先级应该是:先堵住入口(SSH、端口),再加固应用(权限、XML-RPC),最后保证可恢复(异地备份)。不要反过来,很多人花大量时间装安全插件,却把 3306 端口开着。
保姆级实操:上线前逐项执行
步骤 1:创建普通用户并配置 SSH 密钥登录。 全程用密钥,禁用密码登录。
# 本地机器生成密钥对(一路回车即可,或加 -C 备注)
ssh-keygen -t ed25519 -C "admin@mimangfei"
# 把公钥传到服务器(这是唯一需要输一次密码的时候)
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@你的IP
# 登录服务器后创建日常使用的普通用户
adduser webadmin
usermod -aG sudo webadmin
# 把 root 下已授权的公钥复制给新用户
mkdir -p /home/webadmin/.ssh
cp /root/.ssh/authorized_keys /home/webadmin/.ssh/
chown -R webadmin:webadmin /home/webadmin/.ssh
chmod 700 /home/webadmin/.ssh
chmod 600 /home/webadmin/.ssh/authorized_keys
用新用户新开一个终端窗口验证密钥登录成功之后,再继续下一步。 这一步不做完就禁用密码登录,会把自己锁在门外。
步骤 2:修改 SSH 配置,禁用密码与 root 登录。
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo nano /etc/ssh/sshd_config
关键项改成这样:
# /etc/ssh/sshd_config
Port 22922 # 改成 1024-65535 之间的自定义端口,避开扫描器
PermitRootLogin no # 禁止 root 直接登录
PasswordAuthentication no # 禁用密码登录,只允许密钥
PubkeyAuthentication yes
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
MaxAuthTries 3 # 单次连接最多尝试 3 次
LoginGraceTime 30
AllowUsers webadmin # 白名单,只允许指定用户登录
改完后 不要直接重启 sshd,先检查语法,并保持一个已登录的会话不断开:
sudo sshd -t && echo "配置正确" # 无输出即无错误
sudo systemctl restart sshd # Ubuntu 上是 ssh,CentOS 是 sshd
新开终端用新端口测试能连上之后,再关掉旧会话。
步骤 3:配置防火墙,只放行必要端口。
# Ubuntu / Debian 用 ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22922/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw --force enable
sudo ufw status verbose
# CentOS / Rocky / AlmaLinux 用 firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-port=22922/tcp
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
注意:还要在云厂商控制台的”安全组”里同步放行。 云安全组是独立于系统防火墙的一层,两层都放行才通;反过来,只在云控制台开放而系统防火墙没开,也一样连不上。安全组务必检查是否误开了 3306、6379、22 对 0.0.0.0/0。
步骤 4:安装 fail2ban 拦截暴力破解。
sudo apt install -y fail2ban # CentOS: sudo yum install -y fail2ban
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
ignoreip = 127.0.0.1/8
[sshd]
enabled = true
port = 22922
filter = sshd
logpath = /var/log/auth.log # CentOS 改为 /var/log/secure
maxretry = 3
[nginx-http-auth]
enabled = true
[wordpress-login]
enabled = true
port = http,https
filter = wordpress-login
logpath = /var/log/nginx/access.log
maxretry = 5
EOF
# 为 WordPress 登录创建过滤规则
sudo tee /etc/fail2ban/filter.d/wordpress-login.conf > /dev/null <<'EOF'
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php.* 200
^<HOST> .* "POST /xmlrpc.php.* 200
ignoreregex =
EOF
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
步骤 5:收紧 WordPress 文件权限与配置。 目录 755、文件 644,wp-config.php 收到 600,并且禁止 Web 访问敏感文件:
# 在站点根目录执行(把 www-data 换成你的 Web 用户,LNMP 一键包常是 www)
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
sudo chmod 600 /var/www/html/wp-config.php
sudo chown -R www-data:www-data /var/www/html
# Nginx:禁止访问敏感文件
location ~* /(?:wp-config\.php|readme\.html|readme\.txt|wp-config-sample\.php) {
deny all;
}
# 关闭 XML-RPC,挡住 pingback 放大与 system.multicall 爆破
location = /xmlrpc.php {
deny all;
}
# 禁止在上传目录执行 PHP(防图片马 getshell)
location ~* /uploads/.*\.php$ {
deny all;
}
步骤 6:数据库安全加固。 创建专用数据库用户,只给必要权限,不要用 root 用户连 WordPress:
-- 登录 MySQL 后执行,把密码换成强随机密码
CREATE DATABASE wp_site DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY '换成强随机密码';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX
ON wp_site.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
确认 MySQL 只监听本地:
sudo grep -rn "bind-address" /etc/mysql/ 2>/dev/null
# 应为 bind-address = 127.0.0.1,云安全组里绝不能放行 3306
sudo ss -lntp | grep 3306
# 正常应显示 127.0.0.1:3306,出现 0.0.0.0:3306 说明对公网开放了,必须改
步骤 7:开启系统自动安全更新。
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades # 交互界面选 Yes
sudo systemctl enable --now unattended-upgrades
步骤 8:配置异地自动备份。 备份脚本放到另一台机器或对象存储,网站数据备份策略 里有完整的落地方案。最低要求是:每日一次全量、保留 7 天、备份文件不存放在本机 Web 目录。
常见踩坑
- 改了 SSH 端口就重启,没先开新会话验证。 sshd 配置写错会直接断开且连不回去,只能走云控制台的 VNC 救援。正确做法是:
sshd -t检查语法 → 保留当前会话 → 新开终端测试 → 成功后再关旧会话。 - 只配了系统防火墙,忘了云安全组。 表现为”配置都对但连不上”,或者”以为关了其实公网还能扫到 3306″。两层都要查。
- Redis / Memcached 监听 0.0.0.0。 用
ss -lntp检查,必须是 127.0.0.1。未授权 Redis 是”一键沦陷”级漏洞。 - wp-config.php 权限 644 且允许 Web 访问。 数据库账号密码直接暴露。
- 装了一堆安全插件却不更新核心。 漏洞统计里,过时核心与过时插件的占比远高于”没装安全插件”。
- 密钥文件权限太开放导致 SSH 拒绝使用。 本地私钥必须是 600:
chmod 600 ~/.ssh/id_ed25519,否则报错Permissions 0644 for 'id_ed25519' are too open。 - 案例:一台被植入挖矿程序的新服务器。 客户买了服务器后只装了宝塔面板,用了默认的 8888 端口和弱密码,也没改 SSH 默认配置。三天后 CPU 长期 100%,用
top看到一个名为kworkerds的进程占满 CPU,crontab -l里多了一条从境外地址下载脚本的定时任务。这个案例的教训是:宝塔、WDCP 等面板的入口端口本身就是攻击面,要么改掉默认端口 + 强密码 + IP 白名单,要么用完就关掉面板服务(bt stop)。另外,定期执行crontab -l、ls /tmp、ss -lntp这三条命令,能最快发现异常。
总结
安全基线的判断标准是”最小化暴露 + 可恢复”。执行顺序:密钥登录 → 禁 root 改端口 → 防火墙与安全组双层收紧 → fail2ban 拦爆破 → 文件权限与 Nginx 屏蔽 → 数据库专用账号 → 自动更新 → 异地备份。全部做完之后,用 ss -lntp 确认对外只开放 80、443 和自定义 SSH 端口,就算达标。上线后还要持续做 网站监控与告警搭建,配置 CPU 异常与登录失败告警;SSL 证书申请与部署 也是安全基线的一部分——全站 HTTPS 能防止中间人劫持登录凭证。
