凌晨一点,我的博客被 DDoS 了
2026-07-20 写在前面:本文记录了一次因友链交换触发的 DDoS 攻击事件,以及 33 分钟内完成服务器迁移 + Cloudflare 接管的全过程。所有 IP 已脱敏,但踩坑和应急操作全部真实。
起因
博客上线快二十天,当晚刚把 Lighthouse 移动端跑到全满分(性能/无障碍/最佳做法/SEO 全 100),正准备收工睡觉。
想了想,去交换几个友链吧——给站点导点外链权重,SEO 刚起步嘛。
去了一个友链交换平台,提交了博客信息。
然后灾难开始。
攻击
发送我站域名不到十秒,站点就卡了。紧接着一大堆短信接连发到我手机——DDoS 攻击告警、超过防护峰值、机器被封锁、遭受持续攻击延长封锁……
每一条短信都在提醒我:你被打死了,而你能做的只有看着。
我用的是某云厂商的轻量应用服务器,2C2G。它的 DDoS「防护」逻辑是:检测到攻击流量,直接封你的机器,而不是清洗。封 24-72 小时不等。
封机器是它的自保机制——轻量版防护能力有限,怕影响同物理机的其他用户。你的站点就成了牺牲品。
更恶心的在后面:解封前又被打,再次封禁。循环四次。攻击者在持续监控服务器状态,解封就打。这不是随机扫描,是针对性攻击。
决策
等解封?不行。源 IP 已经暴露,解封后还会被持续盯梢。
唯一解:换 IP + 上 Cloudflare 代理隐藏源 IP。
于是临时开了第四台轻量服务器(同账号同地域,内网互通),开始迁移。
迁移
1. 部署后端(3 分钟)
博客是纯 Go 后端 + 原生 HTML/CSS/JS 前端,交叉编译一条命令:
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o jay.pub .
打包二进制 + 模板 + 静态资源 + .env,SCP 上传,配 systemd 服务,启动。后端 3 分钟恢复。
数据库是独立主机部署的(PostgreSQL 在另一台机器,仅内网通信),Web 服务器迁移不影响数据。这是之前架构拆分的红利——四台机器各司其职,Web 挂了换一台就行,数据库不动。
2. Nginx 反代
装 nginx,配置 80 端口反代 8080,assets/uploads 走静态文件。通过公网 IP + Host 头验证,200 OK,后端正常。
3. Cloudflare 接管 DNS
关键一步:把 jay.pub 的 DNS 切到 Cloudflare,开启代理(橙色云朵)。
CF 代理模式下,用户访问的是 CF 的 anycast IP,CF 再回源到我的服务器。攻击者只能看到 CF IP,看不到源站真实 IP。
CF 免费版提供无限流量、无限带宽、无限时长的 DDoS 防护。这是 CF 的核心价值——个人站长也能用上企业级防护。
4. SSL 证书签发(踩坑)
CF 代理模式下,HTTP 验证(acme-challenge)会被 CF 拦截,必须用 DNS 验证签证书。
坑 1:CF API Token 权限
acme.sh 的 CF 插件自动 DNS 验证失败,报 invalid domain。排查发现 CF API Token 的 Zone Resources 没配对,Token 在账号里看不到 jay.pub 这个 zone。
折腾几轮没搞定,决定绕过。
坑 2:泛域名不支持手动验证
acme.sh 的 --dns 手动模式不支持泛域名证书(*.jay.pub),只支持具体域名。先签 jay.pub + www.jay.pub,泛域名以后再补。
acme.sh --issue -d jay.pub -d www.jay.pub --dns \
--yes-I-know-dns-manual-mode-enough-go-ahead-please \
--server letsencrypt --keylength ec-256
acme.sh 输出 2 条 TXT 记录,手动去 CF 后台加:
| 名称 | 类型 | 内容 |
|---|---|---|
_acme-challenge |
TXT | 随机字符串1 |
_acme-challenge.www |
TXT | 随机字符串2 |
注意:TXT 记录的云朵必须点成灰色(DNS only),不能走 CF 代理,否则验证失败。
dig 验证传播后 --renew 完成签发。Let's Encrypt 证书,有效期 90 天,acme.sh 会自动续。
坑 3:nginx http2 语法
nginx 1.24 不支持 http2 on; 指令(那是 1.25.1+ 的语法),要用旧写法:
listen 443 ssl http2; # 旧语法,1.24 可用
# 而不是
listen 443 ssl;
http2 on; # 新语法,1.25.1+ 才支持
5. CF 回源 IP 修正
证书装好,nginx 443 配置完成,但通过 CF 访问还是 522(CF 连不上源站)。
排查发现:CF 后台 jay.pub 的 DNS 记录里,IP 还是旧服务器的。改成新服务器 IP 后立即恢复。
总耗时:33 分钟,从决策迁移到 HTTPS 恢复。
防护现状
迁移到 CF 代理后,攻击者只能看到 CF 的 IP,打的是 CF 边缘节点。CF 全球分布式架构 + 无限流量 DDoS 防护扛住,源站不受影响。
CF 后台记录了多次防护事件,攻击仍在持续但已被挡住。
损失
直接损失
- 旧服务器封禁 24-72 小时
- 旧源 IP 泄露,即使解封也不能再用作博客源站
- 所有登录 session 失效(SESSION_SECRET 换了新值)
- 用户上传的图片未同步(待旧机器解封后补)
间接损失
- 站点在 Google 重新评估期间被打掉,可能影响排名恢复
- 访问用户遇到 522/超时,可能流失
- 从凌晨十二点半折腾到一点半,本该睡觉
经验教训
1. 不要裸奔上公网
个人站点、小项目,一定要上 Cloudflare 代理。免费版足够挡住绝大多数 DDoS。源 IP 一旦暴露,就是被持续攻击的把柄。
正确做法:DNS 解析直接走 CF,橙色云朵代理。源站不暴露公网 80/443 的真实可达性。
2. 友链交换是高危操作
不要在陌生友链平台暴露站点信息。攻击者可能潜伏在这些平台,专门挑新站下手——动机可能是恶意竞争、勒索、纯破坏。
正确做法:
- 友链通过熟人/同行私下交换
- 或在知名社区(V2EX、GitHub)找可信对象
- 不要在「友链交换平台」批量提交站点信息
3. 轻量服务器的 DDoS 防护是假的
某云厂商轻量应用服务器的「DDoS 防护」本质是封你的机器,不是清洗流量。攻击一来,它先自保(封机器保护其他用户),你的站点就成了牺牲品。
正确做法:
- 不能依赖云厂商的轻量 DDoS 防护
- 必须前置一层 CF(或专业清洗服务)
- 源站只允许 CF IP 段访问
4. 备份和快速迁移能力是刚需
这次能在 33 分钟内恢复,靠的是:
- 本地有完整代码仓库(git)
- 交叉编译一条命令出二进制
- 部署清单清晰(二进制 + 模板 + 资源 + .env + version.json)
- 数据库独立部署,不受 Web 服务器影响
如果代码只在被封的机器上,或数据库跟 Web 混部,恢复时间会指数级增长。
5. 文档化部署架构救命
桌面有完整的部署文档:四机架构、内网 IP、凭据、systemd 配置、nginx 配置。迁移时直接照搬,不用现场猜。
正确做法:每次架构变更都更新文档,凭据、IP、端口、配置文件路径全部记录。
防护加固
迁移完成后,继续做了几层加固,确保源站 IP 不泄露、即使泄露也打不动。
1. 吊销 Let's Encrypt 证书
之前用 acme.sh 签的 LE 证书会上报 CT log(Certificate Transparency),任何人都能在 crt.sh 查到「jay.pub 曾经签过 LE 证书」。虽然 CT log 不记录 IP,但攻击者可以用证书指纹在 Censys 历史快照里反查源 IP。
立即吊销:
acme.sh --revoke -d jay.pub --ecc
rm -rf ~/.acme.sh/jay.pub_ecc /etc/nginx/ssl
CT log 记录不可删除,但证书已失效(OCSP revoked),扫描器即使抓到也无效。
2. 改用 Cloudflare Origin CA 证书
CF 后台生成 Origin CA 证书,15 年有效期,SAN 覆盖 *.jay.pub + jay.pub。这个证书不上报 CT log,扫描器扫不到。
CF SSL 模式切换为 Full (strict),CF 验证源站证书链,端到端加密。
踩坑:第一次生成的「客户端证书」没有 SAN,strict 模式报 526。重新生成「源证书」时 Hostnames 填 jay.pub,*.jay.pub,SAN 才正确。526 排查三要素:证书有效期、SAN 匹配、证书链完整。
3. 腾讯云安全组只放行 CF IP 段
最关键的一步。CF 公布了 15 个 IPv4 段 + 7 个 IPv6 段,安全组规则只放行这些网段访问 80/443,其他全部拒绝。
来源 协议 端口 策略 备注
CF IPv4/IPv6 段 TCP 80,443 允许 CF 回源
内网网段 ALL ALL 允许 内网互通
0.0.0.0/0 TCP 22,80,443 拒绝 兜底拒绝公网
效果:攻击者即使知道源 IP,直接打 443 端口,包在网络层被丢弃,TCP 都建立不了,不触发云厂商清洗,不封机器。
重要:云厂商官方明确说「防火墙不能代替 DDoS 防护,非许可 IP 也会触发清洗」。但安全组在网络层丢包,流量根本不到机器,清洗阈值不会触发。这是对的说法。
4. SSH 改内网跳板
公网 22 端口封闭,通过其他同内网机器跳板。本地 ~/.ssh/config 配置:
Host blog-server
HostName <内网IP>
User ubuntu
ProxyJump <跳板机>
ssh blog-server 自动通过跳板机访问。
防护层级总结
攻击者 → CF IP(Anycast 全球节点)
↓ CF DDoS 防护 + WAF + Bot Fight
↓ CF 边缘证书
↓ 回源(加密,CF Origin CA 证书)
源站
↓ 腾讯云安全组(只放行 CF IP 段)
↓ nginx CF 白名单(第二道防线)
↓ Go 后端
三层防护 + 证书不暴露,源 IP 即使泄露也打不动。
时间线
| 时间 | 事件 |
|---|---|
| 00:43 | 发现机器被封,决策迁移 |
| 00:43 | 开新机器,配 SSH |
| 00:45 | 交叉编译,装 nginx |
| 00:48 | 上传文件,systemd 启动,后端恢复 |
| 00:54 | CF DNS 切换,开启代理 |
| 00:59 | CF API Token 折腾(权限错误) |
| 01:09 | 改用手动 DNS 验证 |
| 01:11 | Let's Encrypt 证书签发成功 |
| 01:11 | nginx 443 配置 + HTTP 跳转 |
| 01:16 | CF 回源 IP 修正,HTTPS 恢复 |
| 01:17 | 第二波攻击,CF 拦截 |
| 01:24 | 第四波攻击,CF 拦截 |
| 01:30 | 腾讯云安全组配 CF IP 白名单 |
| 01:40 | 安全组生效,源站直连超时 |
| 01:46 | SSH 改内网跳板 |
| 01:52 | 吊销 LE 证书 + 清理残留 |
| 01:58 | 部署 CF Origin CA 证书(15 年) |
| 02:08 | CF SSL 切 Full (strict),验证通过 |
感想
生气。
凌晨一点,刚把 Lighthouse 跑到全满分,正准备收工睡觉,去交换个友链就被打。一个小博客,没什么商业价值,没什么流量,没有广告没有收费服务,纯用爱发电的站点,就是想认真写点东西,也能被人盯上。
攻击成本太低,防御成本太高。一台 2C2G 的轻量服务器被打一下就被封 24 小时,攻击者可能只是租了个肉鸡随便扫。
但 Cloudflare 救命了。免费版无限流量 DDoS 防护,源 IP 隐藏,全球 CDN 加速。如果没有 CF,这次只能等云厂商解封,然后被持续攻击,循环到放弃。
这次被打了17.48Gbps,只用了不到十秒...而我服务器清洗能力只有2Gbps...
最后——
移除了 Herobrine。