← Back

53 万次撞门:给服务器做了一次安全体检

2026-09-28

起因很平常。我想把家里那台小主机接进刚建好的虚拟内网,顺手去云服务器上翻日志,看看有哪些服务在跑、要不要调整规划。

然后我就愣住了。

这台机器从 8 月 9 号开机到现在,SSH 一共被撞了 32 万次门。

我平时基本不看这些日志。这次认真数了一遍,顺手把该堵的洞都堵了。记一笔,也算给以后的自己留个参考。


一、SSH:32 万次

统计跨度    2026-08-09 → 2026-09-28(50 天)
失败总数    326,373
唯一源 IP   2,131
成功入侵    0

平均每天 6,527 次。头号攻击者是 82.115.30.245(德国法兰克福,Kirino LLC),一个人贡献了 31,977 次,占全站将近 10%。

Aug 13 21:34:16  Connection closed by authenticating user root 82.115.30.245 [preauth]
Aug 13 21:34:18  authentication failure; rhost=82.115.30.245  user=root
Aug 13 21:34:20  Failed password for root from 82.115.30.245 port 55434 ssh2

被猜最多的用户名是 ubuntu(4732)、user(4166)、test(2234)、debian(1893)、deploy(1460)。有意思的是里面还有 minecraft(486) —— 扫描器知道这台机器在开游戏服。

比较好笑的是:95% 的尝试打在了根本不存在的用户名上(73,174 次无效用户名 vs 3,443 次打到真实用户 admin)。

这重要吗?不重要

我的 SSH 早就配好了:

passwordauthentication no
permitrootlogin no
kbdinteractiveauthentication no
permitemptypasswords no
maxauthtries 6

只认 ed25519 密钥。 32 万次撞不开,因为那需要的不是 32 万次,是 2^128 次。

所以这 32 万次的性质是:噪音。它消耗的是我的日志磁盘和 CPU 时间片,不是我的安全性。


二、nginx:一半的流量是扫描

这个才是真让我意外的。

统计跨度     14 天
总请求数     41,126
唯一来源 IP  2,532
404 响应     20,016   (48.7%)

接近一半的请求是 404 —— 也就是说,一半的流量来自扫描器。

 8,194 次  /.env 探测      ← 头号目标
 6,995 次  /.git 探测      ← 第二
   754 次  /wp-* 探测

它们是来找泄漏的密钥的:/.env、/.env.local、/.env.production、/.env.bak、/api/.env、/backend/.env,一共 16 种变体,专找 Laravel / PHP 项目里那个装着数据库密码的文件。


三、一个真实的漏洞

上面那 6,995 次 .git 探测里,有 492 次返回了 HTTP 200。

因为我用 git clone 把站点拉到 /opt/site/ 部署,.git 目录就一起待在了网站根目录里。nginx 老老实实地把它端了出去:

  117  /.git/config     200    ← 泄漏仓库地址和邮箱
   51  /.git/HEAD       200
   27  /.git/index      200
   24  /.git/logs/HEAD  200    ← 泄漏本地提交活动
  142 个 git object 文件被反复下载(共 2,111 次请求)

.git/objects 被下载了 2,111 次。理论上,拿到足够多的 object 就能完整还原仓库历史,包括那些你提交过、后来又删掉的文件。而这个仓库的历史里确实有一批被删掉的旧文件。

但这次影响是低的

我先去查了一件事,再下结论:

$ curl -s https://api.github.com/repos/CarryWS/CarryWS.github.io | grep private
  "private": false,
  "visibility": "public",

仓库本来就是公开的。 那些"被泄漏"的历史文件,任何人在 GitHub 上 clone 一下就能拿到 —— 已经公开很久了。

所以我差点犯了个错:只看"492 次敏感路径返回 200、2,111 次 object 下载",很容易得出"源码仓库被拖库"的惊悚结论。实际去查一下,结论完全不同。

另外也确认了:整个仓库历史里没有任何密钥或口令(唯一命中的是 CSS 里的 input[type=password],以及我博客工具自己写的 BLOCKED 过滤器 —— 说明当初写的时候确实防着这一类)。

不过还是修了,因为 6,995 次探测是实打实的日志噪音,而且万一哪天仓库改成私有,网站根目录里的 .git 就成了真漏洞:

location ~ /\.(?!well-known) {
    deny all;
    access_log off;
    log_not_found off;
}

现在全部 403,网站功能零误伤,Let's Encrypt 的证书续期路径也没被挡住。


四、frp:有人在猜我的 token

157 次  register control error: token in login doesn't match token from configuration
  0 次  client login success

这是我在 7000 端口上暴露的 frp 服务端,有人在反复试 token。

而这正好说明为什么我在同一天把 token 换掉了 —— 不是"以防万一",是确实有人在试。


五、我犯的一个错误

查 SSH 那波的时候,我一度很紧张,跟人说"服务器正在被暴力破解"。

然后被一句话怼回来了:

能暴力破解 id_ed25519 密钥的你是这个👍,比量子计算机还牛

他是对的。 我把"门口有人反复拧门把手"讲成了"门快被踹开了"。

后来认真算了一下:256 位椭圆曲线密钥,暴力破解需要 2^128 量级的运算,宇宙热寂都跑不完。那 32 万次尝试从一开始就不可能成功,它们连"威胁"都算不上,只是背景噪音。

这件事给我的教训不是"要多看日志",而是:看到异常数字时,先搞清楚它的性质,再决定它的严重程度。数字大不等于危险大。

不过顺着这条线,倒是挖出一个真问题:

那 13 万次打到另一台服务器(通过 frp 转发)的爆破,源 IP 全部被 frp 抹成了 127.0.0.1:

106,254 次来自 127.0.0.1

这意味着那边就算装了 fail2ban 也永远封不掉 —— 它看到的攻击源是 localhost,封了等于把自己封了。这是一个无法通过配置修复的结构性缺陷。

关掉 frp、改用 WireGuard 之后,真实源 IP 被保留了(现在是 10.8.0.2),而且攻击流量直接归零。


六、顺手做成的事

这次本来只是"想把第二台服务器接进虚拟内网",结果一路做下来变成了安全整改:

✅ 关掉 frp          → 消除了 133,900 次爆破 + 157 次 token 猜测的入口
✅ 轮换弱凭据        → token 和面板密码换成随机值
✅ 补上 nginx 拦截   → .git / .env 全部 403
✅ 三节点虚拟内网    → 10.8.0.1 / .2 / .3 互通,SSH 不再暴露公网

第三台机器(家里那台 M720q)现在是 10.8.0.3,从公网完全不可达:

113.27.x.x:22     closed
113.27.x.x:8787   closed
113.27.x.x:6099   closed
113.27.x.x:25565  closed

网站的性能监测卡片也顺势从 frp 转发改成了 nginx 反代走 WireGuard 隧道 —— 路径、域名、CORS 全不变,网页代码一个字没改,但现在它走 HTTPS 了。


七、几条结论

1. 互联网背景噪音是常态,不是事件

两台机器加起来 53 万次认证失败,没有一次是冲着我来的。都是自动化扫描器在 IPv4 地址空间里无差别地扫。

而且我观察到一个有意思的现象:扫 Web 的和撞 SSH 的是两拨完全不相干的人。头号 SSH 攻击者在 nginx 上 0 次记录,头号 Web 扫描器在 SSH 上 0 次记录。这是两个独立的地下产业,各自扫各自的。

2. 真正的安全边界是配置,不是监控

我几十天没看日志,代价是什么?几乎为零。 因为 PasswordAuthentication no 这一条配置就把整类攻击彻底废掉了。

反过来,如果密码登录开着,我每天看十遍日志也没用 —— 早晚会被撞开。

3. 数字大不等于危险大

32 万次、48% 的 404、2,111 次 object 下载,每一个单看都很吓人。但查清楚性质之后:一个是无效的,一个是背景噪音,最后一个因为仓库公开所以无影响。

先查证,再定性。

4. 有些东西是"看起来没坏但已经坏了"

frp 那个源 IP 丢失的问题,不查日志根本发现不了。它不影响任何功能,只是让你的防御手段静默失效。这类问题最值得花时间找。


差不多就这些。写下来主要是提醒自己:服务器跑起来容易,跑得干净是另一回事。

(顺便,这次把一个正在被境外 IP 爆破的暴露面彻底干掉了 —— 从"看不见"变成"不存在",比从"看不见"变成"看得见"有用得多。)

服务器安全WireGuard运维