凌晨两点多,手机连着响了七八声,全是监控报警。登录服务器看了一眼,整个/var/log/secure文件刷得飞快,全是Failed password for root,一秒好几条,来自同一个 IP,连着试了十几个用户名。不用猜,这是典型的暴力破解,密码字典正在扫我的 SSH 端口。
那天晚上我处理完已经是凌晨五点,事后写了一份排查记录,顺手把这段时间反复踩过的坑、验证过的方案都整理出来。这篇文章不写空话,全是实际操作,从发现入侵、止血止损,到日志取证、加固防护,最后到长期监控,一步步给你拆开讲。如果你也遇到类似情况,按这个流程走一遍,至少不会再手忙脚乱。
1. 事件定性:先搞清攻击到了哪一步
1.1 暴力破解攻击的本质
暴力破解这个东西,原理一点都不神秘。攻击者手里有一份很大的字典文件,里面装满了常见的用户名和密码组合,比如admin/123456、root/password、test/test之类的,然后用脚本一遍一遍往你服务器的 SSH 端口发登录请求。密码对了就能登进去,不对就换下一个。单个请求只有几百字节,攻击机带宽稍微好一点,一秒钟就能打几十上百次尝试。
这类攻击有三个明显特征,你在日志里一眼就能认出来:
- 同一个 IP 在短时间内发起大量登录失败请求,频率远超正常人手动操作;
- 用户名字典组合是「扫射式」的,
root、admin、test、oracle、ubuntu轮着来,有时候还会带大小写变种; - 攻击时间往往集中在下半夜或者业务低谷期,因为爆破会产生大量日志,白天的运维容易第一时间发现。
我这次遇到的就是典型的第一种,一个 IP 在十分钟内发了快八百次登录请求,用的用户名列表覆盖了几十个常见账号。好在服务器上的root密码是 28 位随机生成的,密码策略也强制启用了,所以密码本身没被怼开,但光是这种日志刷屏和连接风暴,就够让人头疼的。
1.2 入侵影响面评估:别急着下结论
很多人看到暴力破解日志第一反应是「完了,被入侵了」,然后慌慌张张去断网、重装系统,结果证据没保存,业务也停了。正确的做法是先冷静评估攻击是否成功,分三档处理:
- 阶段一(仅尝试,无成功):日志里全部是
Failed password,没有看到Accepted password,说明攻击者还在撞库阶段,内部系统没有被突破。处理方式以阻断和加固为主。 - 阶段二(存在成功登录记录):日志里出现了
Accepted password for 某用户 from 某IP,说明至少有一个账号被攻破了。这时候要立刻隔离服务器,防止横向扩散。 - 阶段三(已经执行了命令且可能有后门):不仅是登录成功,
/root/.bash_history或审计日志里还出现了可疑命令,比如下载脚本、添加用户、修改定时任务等。这时候已经不适合线上排查了,建议镜像保留现场后,重装系统或从干净备份恢复。
我当时对照日志查了一圈,确认是阶段一,才敢继续原地排查。这个判断非常关键,因为它决定了后续所有操作的风险等级。
提示:暴力破解和价值直接相关的服务器(数据库、钱包节点、权限管理后台)不止是登录尝试,还要检查是否有登录成功后提权行为。判断入侵程度一定基于日志证据,不要靠猜测。日志取证在任何步骤之前。
2. 应急止损:攻击还在继续,第一时间做什么
2.1 立即阻断攻击源头
先说最紧急的:把当前正在攻击的 IP 断掉。这一步不需要精细操作,谁打你你关谁,动作要快。最直接的手段是操作系统的防火墙,比如用iptables、firewalld或ufw临时拉黑该 IP:
# 使用 iptables 临时封禁攻击 IP(仅阻断,不删除规则) iptables -I INPUT -s 5.188.206.32 -j DROP # 如果用的是 firewalld firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="5.188.206.32" drop' firewall-cmd --reload # 使用 ufw 封禁 ufw insert 1 deny from 5.188.206.32我在实际处理中用的就是iptables -I INPUT的方式,-I表示把规则插到最前面,旧的规则不冲突就能立刻生效。如果攻击源 IP 一直在变,比如扫了整个段,可以先临时封掉最活跃的几个,再接着做下面的加固。
需要解释一下为什么不直接改 sshd 配置来止血——因为改配置要重启服务,重启期间新连接会断,如果攻击流量还在打,可能出现连接雪崩。防火墙封 IP 是即时生效的,对存量连接不影响,对攻击流量直接丢弃,处理优先级应该最高。
2.2 切换 SSH 端口并限制登录
把攻击流量暂时挡住之后,下一个动作就是把 SSH 默认的 22 端口换掉。暴力破解脚本百分之九十九是默认扫 22 端口,改了端口相当于把大门从主干道搬到小巷子里,哪怕密码简单,也会把绝大多数扫描挡在门外。
编辑/etc/ssh/sshd_config:
# 修改前先确认新端口没有被占用 # ss -lntp | grep 2022 Port 2022然后重载服务:
sshd -t && systemctl reload sshd这里有两个非常关键的细节,踩过坑的都懂:
- 改端口前一定要先把自己当前的 SSH 连接保住。我是开着两个终端窗口操作的,一个改了配置先不重载,另一个用
ssh -p 2022做连接测试,测试通过了再重载。否则一旦配置写错,重载后原连接断掉,你只能去物理机或者云厂商的 VNC 控制台救急。 - 改端口要记得同步改云安全组。我用的云服务器的安全组策略默认只放行了 22 端口,如果开了防火墙却没改云平台策略,新的 2022 端口照样连不上,而且这个问题容易和系统防火墙混淆,排查半天才发现是安全组没加。
同时把/etc/ssh/sshd_config里的PermitRootLogin改成no,关闭 root 直接登录。原因很简单:root 是操作系统的超级账号,暴力破解字典里 99% 会包含root这个用户名。关掉 root 直登并不会让你没法管理服务器,日常运维完全可以先登录普通用户,再通过sudo切换到 root。这样一来,攻击者扫到 root 也无门可入,只能去猜普通用户名,成本翻了好几倍。
我还顺手把LoginGraceTime改成了 30 秒,超过 30 秒不认证就断开。这样做的好处是防止有人挂起大量半连接来占满 sshd 的进程,算是一种轻量的防扫描手段。
2.3 检查是否存在异常账号和后门
止血不止是封 IP、改端口。攻击如果已经深入到了系统内部,你光堵住入口没有用,对方可能已经留了后门。所以在端口和密码问题处理完之后,必须要对账号和计划任务做一次快速体检。
检查账号:
# 列出了所有用户,确认没有异常新增账号 awk -F: '$3==0{print $1}' /etc/passwd # 检查是否有空密码账号,这类账号等于裸奔 awk -F: '($2==""){print $1}' /etc/shadow检查计划任务:
# 系统级计划任务 ls -la /etc/cron.d/ /etc/cron.daily/ /var/spool/cron/ # 当前用户计划任务(root 和其他用户都看一下) crontab -l检查自启动项:
# 查看已经加载的服务,注意和正常业务对比 systemctl list-unit-files --type=service --state=enabled这轮检查的目的是确认攻击者有没有在系统里「扎根」。如果发现不明账号、可疑的 cron 任务、新增的服务,说明攻击已经不止是爆破登录这么简单了。对于阶段一、阶段二的攻击,这些检查结果应该是干净的。我在处理时确认了/etc/passwd里没有uid=0的新账号,crontab -l输出为空,/root/.bash_history只有自己的操作记录,才放下心来进入下一步排查。
3. 日志取证:还原攻击者的完整操作路径
3.1 关键日志文件和取证命令
攻击源封了,后门查了,下一步要做的是把日志完整保存下来,作为事件分析和后续安全审计的依据。取证这件事很多人觉得无所谓,真出问题的时候才后悔没存日志。
Linux 系统里和 SSH 登录相关的日志文件主要看发行版:
- CentOS / RHEL:主要看
/var/log/secure,里面记录了完整的安全认证事件; - Ubuntu / Debian:主要看
/var/log/auth.log,作用和secure一样; /var/log/messages或journalctl:可以看系统整体状态,但一般不需要全量翻。
我先做的是把日志文件直接复制一份出来,备份到一个安全目录:
mkdir -p /opt/security_evidence/$(date +%Y%m%d%H%M%S) cp /var/log/secure /var/log/auth.log /opt/security_evidence/$(date +%Y%m%d%H%M%S)/ 2>/dev/null然后开始分析。第一步是看成功登录的记录,这个优先级最高。命令:
# 在 CentOS 上查成功登录记录 grep "Accepted password" /var/log/secure | awk '{print $1,$2,$3,$9,$11}' # 在 Ubuntu 上 grep "Accepted password" /var/log/auth.log | awk '{print $1,$2,$3,$9,$11}'输出这样的记录:
Feb 18 03:12:22 root 5.188.206.32 Feb 18 03:13:45 root 5.188.206.32看到这两条的时候我愣了一下,因为刚才已经确认过没有 Accept 记录,这可能是攻击者尝试成功后又退出了。继续往下查发现是同 IP 的多次尝试,中间有一次密码确实匹配了——这就属于阶段二了。排查时要特别留意这种「一次成功 + 多次失败」的混合模式,攻击者可能用正确密码进来过一次,然后再用别的字典继续试,就是为了确认账号有效性。
3.2 用命令快速统计攻击特征
日志文件动辄几万行,人肉翻不现实。我做了个简单的统计,把攻击源的 IP、尝试次数和被尝试的用户名列出来,很快就看清了整体攻击模式。
统计攻击 IP 和次数:
grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -20我拿到的最前面的输出大概是:
812 5.188.206.32 126 10.2.4.77 43 103.74.192.15统计被尝试的用户名:
grep "Failed password" /var/log/secure | awk '{print $(NF-5)}' | sort | uniq -c | sort -nr | head -20输出会清晰显示攻击者字典里的用户名分布,比如:
512 root 138 admin 96 test 45 oracle 23 tomcat这些统计数字有两个用途:一是给攻击定性写报告用;二是如果你需要去云平台投诉或找网警报案,有明确的证据链。我看到 top 用户的分布后,也顺带把系统里所有账号密码重新过了一遍——尤其检查了root、admin、test这些账号是不是还在用弱密码。接着我上调了所有账号的密码复杂度要求,防止攻击者字典里真就撞上一个。
3.3 顺藤摸瓜排查可疑行为轨迹
暴力破解不一定是攻击的最终目的。有些攻击者成功登录后会立刻执行某些命令,比如下载挖矿程序、替换系统命令、创建隐藏账号。我建议从成功登录的时间点往后推,把/var/log/messages里相关时间段的异常行为也拉出来看看。
具体做法是把成功登录时间点前后五分钟的日志单独切出来:
# 先记录成功登录的时间点 grep "Accepted password" /var/log/secure # 然后以这个时间为锚点查前后日志 grep "Feb 18 03:1[2-5]" /var/log/secure | grep -v "Failed password"还要检查 shell 历史记录。正常人是不会在服务器上手动敲wget+ 脚本下载地址的,但自动化攻击工具会。用这个命令排查:
cat /root/.bash_history | grep -E "wget|curl|nc |/dev/tcp|chmod \+x|useradd|passwd"如果发现有下载脚本、添加账号、开放端口的操作记录,那就要按阶段三来升级处理了。这里必须强调:先获取和保存证据,再考虑处置动作。很多新手一看到可疑命令,立刻删文件、杀进程、改密码,结果操作完了发现没法追溯攻击者到底干了什么。合理的顺序是备份日志、记录时间点、截图取证,再做处置。
4. 系统加固:把暴力破解的门彻底堵死
4.1 密码策略和账号安全配置
暴力破解攻击成功的唯一前提是密码被猜中。那么把密码做得极其难猜,就算日志里刷满失败记录,攻击者也进不来。这一步是我在事件后花时间最多的地方。
修改 Linux 密码策略需要用到pam模块。我直接改/etc/security/pwquality.conf和/etc/login.defs,把密码最小长度设为 16 位、同时启用复杂度和新旧密码不能相同的限制:
# /etc/security/pwquality.conf minlen = 16 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1每行的含义拆开讲一下:
minlen=16:密码最短 16 位;dcredit=-1:至少包含 1 个数字;ucredit=-1:至少包含 1 个大写字母;lcredit=-1:至少包含 1 个小写字母;ocredit=-1:至少包含 1 个特殊字符。
/etc/login.defs里还有一个叫PASS_MAX_DAYS的参数,我改成了 90,意思是密码每 90 天必须换一次。配合PASS_MIN_DAYS设为 7,防止用户改回原密码。这样就算某个密码意外泄露,有效期最多 90 天,到期后攻击者手里那张「旧门票」自动作废。
同时把所有用户密码重新生成了一遍。生成密码的规则是保长度、弃规律,我是通过系统内置的随机数发生器生成的,而不是人工去编一段「好记」的密码。人工编密码的规律性太强,字典攻击很容易命中模式;随机密码没有模式可言,暴力破解的复杂度指数级上升。
改完密码策略后,顺手把账号过期策略加上了。对长期不用的僵尸账号,直接锁定:
# 一键设置用户 must change password at next login chage -d 0 用户名 # 直接锁定长期不用的账号 usermod -L 用户名4.2 启用 SSH 密钥认证并彻底关闭密码登录
密码再长也有被撞的可能,密钥认证则完全是另一套思路。客户端保存一把私钥,服务器保存对应的公钥,登录时用非对称加密做签名验证。攻击者没有私钥文件根本登不进来,暴力破解在密钥认证面前没有任何意义。
部署步骤:
在本地生成密钥对(客户端机器操作):
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/某服务器_ed25519-t ed25519指定使用椭圆曲线算法,安全性高且密钥短;-a 100是将 KDF 迭代次数设置为 100 轮,强行拖慢暴力破解的尝试速度。生成后把公钥上传到服务器:
ssh-copy-id -p 2022 -i ~/.ssh/某服务器_ed25519.pub 用户名@服务器IP完成后在服务器端修改 SSH 配置:
PubkeyAuthentication yes PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no然后测试密钥登录,确认能顺利登录后,再重载 sshd:
sshd -t && systemctl reload sshd这里有个容易踩的坑:先测密钥登录能通,再关密码认证。我上次给别人做安全加固时,对方远程操作,直接改了配置关掉密码认证,结果密钥没配好,他自己也登不进去了。最后只能通过云厂商管理终端进入恢复,折腾了半小时。正确的顺序一定是先传公钥-测密钥-再关密码。说道说道为什么UsePAM no——PAM 模块默认会额外做密码验证,关了它才能保证密码认证无效化。
4.3 部署 fail2ban 自动封禁恶意 IP
密码策略和密钥认证搞定后,总会有那么些 IP 不死心继续扫。与其每次人工封,不如让系统自己学会识别并封禁。fail2ban就是干这个的。
fail2ban的原理很简单:它持续监控日志文件,如果发现某个 IP 在设定时间内触发多次失败事件,就调用防火墙规则自动封禁该 IP,并在封禁期结束后自动解封。这相当于给 SSH 加了一层「免疫功能」。
安装和配置:
# CentOS yum install fail2ban -y # Ubuntu apt install fail2ban -y在/etc/fail2ban/jail.local配置监控 SSH 服务的规则:
[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 5 bantime = 3600 findtime = 600每个参数的作用:
maxretry=5:10 分钟(findtime)内失败 5 次就触发封禁;bantime=3600:封禁 3600 秒,也就是 1 小时;filter=sshd:使用系统自带的 sshd 过滤规则;logpath:指定日志路径,Ubuntu 上要改/var/log/auth.log。
启动之后我可以看到攻击 IP 开始陆陆续续进入封禁名单:
fail2ban-client status sshd输出能看到:
Status for the jail: sshd |- Filter | |- Currently failed: 3 | |- Total failed: 26 `- Actions |- Currently banned: 4 |- Total banned: 12我一直把bantime设为 3600 秒而不是永久封禁,原因是动态 IP 环境下,任何用户(包括你老板、客户、供应商的 IP)都有可能在触发阈值后误封。永久封禁一旦误伤,解封流程会非常痛苦。1 小时短封既能有效打断攻击,又能在误封后快速自愈。另外,我自己测试登录时经常连续输错密码,很容易触发规则把自己封掉,所以封禁时间拉长到 24 小时反而更麻烦。
4.4 防火墙和云安全组双重过滤
fail2ban自动封 IP 是应用层的机制,它的底座还是防火墙。我在系统防火墙层面也做了几件事,让针对性扫描难度更大。
首先是开防火墙端口白名单策略。如果这台服务器只对少数几个办公 IP 开放 SSH,那就用firewalld的富规则做来源限制:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="办公IP/24" port port="2022" protocol="tcp" accept' firewall-cmd --permanent --remove-service=ssh firewall-cmd --reload只允许公司出口 IP 段访问 2022 端口,其他来源一律拒绝。对于单一管理员管理的服务器,这个方案最有效。如果服务器有多地办公人员需要 SSH 访问,至少也要做到按端口限制,而不是0.0.0.0/0全放。
云安全组同样可以设置。登录云平台控制台,在安全组规则里修改入站规则:
- TCP 22 端口,来源
0.0.0.0/0:移除; - TCP 2022 端口,来源
办公IP段:保留。
这个动作的意义在于:云安全组是在虚拟化层拦截的,比系统防火墙更早发挥作用,攻击流量根本进不到你的 ECS 实例内部。两个防火墙叠加,攻击者连探测端口是否开放都会遇到困难,系统日志里失败记录都要少一大半。
5. 长期监控与事件复盘:安全不是一次性的
5.1 建立主动监控和告警
暴力破解不可能根除,你的服务器放在公网上,每天都会接收到各种扫描。真正有用的不是追求「零攻击」,而是做到「攻击早发现、失败不造成后果」。所以长期监控必须有。
我常用的是auditd配合 shell 脚本做关键文件监控,效果不错。先安装并启动审计服务:
apt install auditd -y systemctl enable auditd --now然后添加关键文件的审计规则:
auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config这条规则的意思是:监控sshd_config的写入和属性变更,事件标记为sshd_config。一旦有修改动作,会记录在/var/log/audit/audit.log里。这样就算有人动了 SSH 配置,你也能在事后查到是什么时间点、被什么进程改的。
日志集中采集我也在推进。我的方案是给每台服务器装一个轻量日志采集代理,把/var/log/secure、/var/log/auth.log、/var/log/audit/audit.log实时推到集中日志系统。这样日志不会因为服务器被入侵而丢失,查询分析也更方便。
如果担心一开始配置太复杂,可以先做一个简单版本的监控脚本,每分钟检查一次失败登录次数,超过阈值就发告警:
#!/bin/bash fail_count=$(grep "Failed password" /var/log/secure | wc -l) if [ "$fail_count" -gt 200 ]; then echo "检测到高频暴力破解,当前失败次数: $fail_count" | mail -s "SSH 暴力破解告警" admin@example.com fi告警渠道可以用邮件、企业微信机器人、短信。我个人的体会是短信优先级最高,邮件容易被忽略,微信机器人适合白天用。告警阈值要结合业务特点调,我这边设的是 10 分钟内 SSH 登录失败超过 50 次立即告警,平时不太会误报,真被刷的时候能第一时间通知到我。
5.2 事件复盘报告和后续改进
事件处理完,先别急着庆祝,下一步是写复盘报告。每次安全事故都有反复出现的点,写报告的过程能帮你把整个处理链路捋清楚。
复盘报告我按五个问题来组织:攻击源是什么、利用了什么薄弱点、影响范围多大、应急措施有哪些不足、后续如何防止类似事件。
我这次事件里暴露出两个重要问题:
第一是密码策略不够严:虽然 root 密码是随机长密码,但有几个运维同事共用的普通用户密码仍然偏弱,且从来没换过。这个漏洞排查时差点漏掉。事后强制改密并加上有效期,才真正补上。
第二是监控缺失:攻击刷了几百次日志,我之前毫不知情,直到看报警才发现。这说明部署集中监控和告警是长期安全的基础,不是可有可无。
另外记录一个经验:复盘时尽量不要只写自己的操作记录,要把时间线、关键日志证据、操作命令、处理结果全部附上。这一方面方便以后同类问题快速定位,另一方面如果有人需要向管理层汇报或与云厂商协同处理,材料拿得出手。
5.3 推荐的最低安全基线清单
如果对安全加固还没有成体系的方案,建议直接把下面这份最小化检查清单当模板,逐条对照落地:
| 检查项 | 操作说明 | 状态 |
|---|---|---|
| SSH 密码认证 | 已改为密钥认证,PasswordAuthentication no,已验证可正常登录 | 待确认 |
| root 远程登录 | PermitRootLogin no,日常使用普通用户 + sudo 切换 | 待确认 |
| 密码复杂度 | pwquality.conf设置 minlen=16,启用数字/大小写/特殊字符组合 | 待确认 |
| 账号清理 | 逐项检查/etc/passwd,锁定所有不用的账号,删除空密码账号 | 待确认 |
| 自动封禁 | fail2ban 配置maxretry=5, bantime=3600, findtime=600 | 待确认 |
| 防火墙策略 | SSH 端口只对办公 IP 段开放,云安全组同步限制 | 待确认 |
| 日志审计 | auditd 监控/etc/ssh/sshd_config等关键文件 | 待确认 |
| 告警通知 | 失败次数超过阈值自动推送告警到手机/即时通讯工具 | 待确认 |
做完这一套,后面再遇到暴力破解就会轻松很多。攻击者扫一轮发现打不进来,自然会转向下一个目标。服务器安全不是买一个防入侵系统装上去就完事,它需要从账号、端口、认证方式、监控告警等多个维度一起构建,才能把风险压到一个可以接受的水平。
我在实际处理中还有一个体会:遇到攻击先别慌,先把事实搞清楚,再按「止损 -> 取证 -> 加固 -> 复盘」的顺序一步步走。这套方法论对任何一台服务器都适用。如果你现在的服务器还没有做密钥认证、密码策略也比较宽松,就从今天开始调整,别看不上这半小时的配置工作——真被爆破成功的时候,代价绝对不止这半小时。