从删库到全面加固:一次真实MySQL“删库勒索”事件的完整记录与安全防御实战 凌晨收到求助“数据库只剩 RECOVER_YOUR_DATA 了。” 这是一次典型的“删库勒索”攻击。本文完整记录了从发现问题、数据恢复失败、彻底安全加固、容器重建、Navicat SSH隧道排错到安全组自动化白名单管理的全过程内含大量可直接落地的实操命令与排错思路。一、事故现场熟悉的噩梦开局某天下午一位读者发来截图执行show databases;后原本的“共享充电宝”业务库不见了取而代之的是一个陌生的数据库mysql show databases; ---------------------- | Database | ---------------------- | RECOVER_YOUR_DATA | | information_schema | | mysql | | performance_schema | | sys | ----------------------这个RECOVER_YOUR_DATA就是臭名昭著的“删库勒索”攻击标志—— 攻击者通过暴力破解弱密码进入数据库执行DROP DATABASE后留下这个库作为联系方式索要比特币赎金。二、诊断过程问题定位四步走1. 排除“显示问题”检查了客户端过滤设置Navicat/DataGrip排查了权限问题SHOW GRANTS FOR CURRENT_USER();确认连接到了正确的服务器和端口 —— 结果显示不是显示问题是真的被删了。2. 检查容器挂载路径读者使用的是Docker 部署的 MySQL容器名为share_mysql映射端口为3307。查看挂载信息docker inspect share_mysql | grep -A 10 Mounts找到宿主机上的数据目录Source路径后检查物理文件ls -lh /宿主机路径/ | grep 数据库连接的名字结果文件夹已经不存在了—— 说明DROP DATABASE已触发操作系统层面的文件删除。3. 检查 Binlog检查是否开启了二进制日志ls -lh /宿主机路径/ | grep -E mysql-bin|binlog结果没有 binlog 文件—— 软件恢复手段宣告失败。4. 检查阿里云快照登录阿里云控制台 - 快照列表检查是否有历史快照。结果没有任何历史快照—— 最终确认数据无法恢复。三、为什么会被攻击根因分析两个致命问题同时存在问题具体表现风险等级弱密码MySQL root 密码为弱口令如root、123456 致命端口暴露安全组对 3307 端口放行0.0.0.0/0 致命这两个问题的组合等于把数据库大门敞开并贴上了“密码是123456”的纸条。黑客用自动化脚本扫描全网 3306/3307 端口发现弱密码后直接执行DROP DATABASE。四、数据恢复无望后的决策读者确认该库中的数据无需恢复是测试环境数据我们决定彻底清理旧环境用最安全的方式重新搭建数据库全面加固安全配置配置自动备份策略五、安全加固方案完整实战 第一阶段阿里云安全组收紧最外层防线安全组规则的“叠加陷阱”必看在配置安全组时有一个极其容易踩坑的地方安全组规则是叠加生效的新添加的规则不会覆盖旧规则也就是说如果你同时存在两条规则规则 A允许0.0.0.0/0访问 8848 端口规则 B允许你的IP/32访问 8848 端口那么全世界的 IP 依然能访问因为规则 A 仍然有效。正确的做法是先删除所有0.0.0.0/0的旧规则再添加特定 IP 的新规则或者在新规则添加后手动删除旧规则。删除所有“万能钥匙”规则以下端口的0.0.0.0/0规则全部删除或收紧端口处理方式3306/3307 (MySQL)删除或改为 IP 白名单6379 (Redis)删除8848 (Nacos)删除或改为 IP 白名单9090/9092/9093删除用不到的话24386删除3000/8080删除或改为内网授权3389 (RDP)删除Linux 用不到安全组配置界面的正确填写方式在阿里云安全组“入方向”添加规则时字段正确填写常见错误访问来源你的公网IP/32如122.xx.xx.xx/32选了0.0.0.0/0或172.16.0.0/12访问目的端口8848/8848或3307/3307选错端口关键提醒172.16.0.0/12是阿里云内网网段你的家用电脑不在此范围内选了这个会导致无法访问最终安全组规则建议端口授权对象用途备注22 (SSH)你的公网IP/32远程管理不要用0.0.0.0/080 (HTTP)0.0.0.0/0网站访问如需对外服务才保留443 (HTTPS)0.0.0.0/0网站加密访问如需对外服务才保留其他所有端口无规则—全部禁止访问⚠️操作顺序警告先新增自己的 IP 授权规则再删除旧规则防止操作中断连把自己锁在门外。特别提醒80/443 端口绝对不能改成个人IP如果有网站对外服务80 和 443 端口必须保持0.0.0.0/0否则全世界只有你能打开网站。 第二阶段启用阿里云自动快照最后的保险在阿里云 ECS 控制台配置自动快照策略配置项建议值策略名称Daily_MySQL_Backup重复日期每天周一至周日开始时间凌晨 02:00 (UTC8)保留时间7 天自定义时长配置完成后务必“关联资源”将策略绑定到存放 MySQL 数据的云盘。小技巧配置完成后建议立即创建一个手动快照作为当天的兜底保障。 第三阶段彻底重建 MySQL 容器1. 清理旧容器和数据# 删除旧容器 docker stop share_mysql 2/dev/null docker rm share_mysql 2/dev/null # 清理旧数据目录谨慎操作 rm -rf /data/mysql_data2. 创建新容器端口映射到宿主机本地回环docker run -d \ --name share_mysql \ -p 127.0.0.1:3307:3306 \ -e MYSQL_ROOT_PASSWORD自己设置的强密码 \ -v /data/mysql_data:/var/lib/mysql \ --restartalways \ mysql:8.0 \ --bind-address0.0.0.0 \ --default_authentication_pluginmysql_native_password关键参数解读-p 127.0.0.1:3307:3306只将端口绑定到宿主机的本地回环外部网络无法直接访问--bind-address0.0.0.0容器内监听所有接口确保端口映射生效没有0.0.0.0/0的安全组规则数据库端口对外完全不可见3. 验证容器运行状态docker ps | grep share_mysql netstat -tulpn | grep 3307 # 应该看到 127.0.0.1:3307 LISTEN 第四阶段MySQL 账号权限配置查看当前账号进入容器docker exec -it share_mysql mysql -uroot -p输入 root 密码后执行SELECT Host, User FROM mysql.user;初始状态下只有rootlocalhost和几个系统账号。创建远程访问账号解决 Navicat 连接报错由于 SSH 隧道转发后MySQL 看到的来源 IP 是 Docker 网桥网关172.17.0.1而非localhost因此需要创建允许该来源连接的账号。方式一创建root%允许任意主机CREATE USER root% IDENTIFIED BY 自己设置的强密码; GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION; FLUSH PRIVILEGES;方式二创建专用应用账号更安全推荐生产环境CREATE USER app_user% IDENTIFIED BY AppPwd_2026#Secure!; GRANT ALL PRIVILEGES ON charging_business.* TO app_user%; FLUSH PRIVILEGES;方式三精确指定来源 IP最安全CREATE USER my_mysql172.17.0.1 IDENTIFIED BY 123456; GRANT ALL PRIVILEGES ON charging_business.* TO my_mysql172.17.0.1; FLUSH PRIVILEGES;六、Navicat 连接配置SSH 隧道方式—— 完整排错实战这是整个过程中最曲折、最考验耐心的部分也是本文最精华的内容。我把每一步遇到的报错和解决方案都记录下来。 排错全流程问题 1容器创建失败 —— 端口被占用报错信息Error starting userland proxy: listen tcp4 127.0.0.1:3306: bind: address already in use原因宿主机 3306 端口被其他进程如原生 MySQL 或其他容器占用。解决方案改用 3307 端口映射。docker run -d \ --name share_mysql \ -p 127.0.0.1:3307:3306 \ # 改为 3307 ...问题 2容器创建失败 —— 容器名被占用报错信息Conflict. The container name /share_mysql is already in use原因之前执行docker run虽然失败了但容器已创建处于停止状态名称被锁定。解决方案强制删除残留容器。docker rm -f share_mysql问题 3SSH 隧道认证失败报错信息Access denied for password. Authentication that can continue: publickey, gssapi-keyex, gssapi-with-mic, password原因Navicat SSH 标签页里填的密码是MySQL 密码而不是Linux 服务器登录密码。这两个密码是完全独立的。解决方案在 SSH 标签页中填写 Linux 服务器的登录密码即ssh root公网IP时输入的密码。问题 4MySQL 连接被拒绝 —— Host not allowed报错信息1130 - Host 172.17.0.1 is not allowed to connect to this MySQL server原因rootlocalhost只能从本地连接而 SSH 隧道转发后来源 IP 是 Docker 网桥的172.17.0.1。解决方案创建允许从任意主机或指定 IP连接的账号。CREATE USER root% IDENTIFIED BY 自己设置的强密码; GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION; FLUSH PRIVILEGES;问题 5使用my_mysql账号连接时密码错误报错信息1045 - Access denied for user my_mysql172.17.0.1 (using password: YES)原因my_mysql172.17.0.1账号存在但 Navicat 里填的密码与 MySQL 中存储的密码不一致。解决方案重置该账号的密码。-- 先确认账号是否存在 SELECT Host, User FROM mysql.user WHERE Usermy_mysql; -- 如果存在重置密码 ALTER USER my_mysql172.17.0.1 IDENTIFIED BY 123456; FLUSH PRIVILEGES; -- 如果不存在创建并授权 CREATE USER my_mysql172.17.0.1 IDENTIFIED BY 123456; GRANT ALL PRIVILEGES ON charging_business.* TO my_mysql172.17.0.1; FLUSH PRIVILEGES;✅ 最终成功的 Navicat 配置SSH 标签页加密隧道字段填写内容注意事项使用 SSH 隧道✅ 勾选必须开启主机名/IP地址阿里云 ECS 公网 IP不是 127.0.0.1端口22SSH 默认端口用户名Linux 系统登录名如root不是数据库用户名密码Linux 系统登录密码不是MySQL 密码常规标签页MySQL 连接字段填写内容注意事项主机名127.0.0.1固定不变端口3307与容器映射端口一致用户名root或my_mysql取决于你使用的账号密码对应的数据库密码与 SSH 密码不同 验证命令汇总在 ECS 终端执行以下命令快速排查问题# 1. 检查容器是否运行 docker ps | grep share_mysql # 2. 检查端口是否监听 netstat -tulpn | grep 3307 # 3. 容器内测试数据库账号 docker exec -it share_mysql mysql -uroot -p # 4. 查看 MySQL 用户列表 docker exec -it share_mysql mysql -uroot -p -e SELECT Host, User FROM mysql.user;七、Nacos 公网访问的安全陷阱在加固过程中读者还遇到了一个典型问题明明在安全组中配置了“仅允许本机IP访问 8848 端口”但 Nacos 控制台仍然可以被任何人访问。 问题根源这里存在两个层面的原因原因一安全组规则叠加90% 的情况安全组规则是叠加生效的不是“覆盖”关系。如果同时存在规则 A0.0.0.0/0允许访问 8848 端口规则 B你的IP/32允许访问 8848 端口那么全世界的 IP 依然能访问因为规则 A 仍然有效。✅正确做法先删除所有0.0.0.0/0的旧规则再添加你的IP/32的新规则。原因二服务监听地址为 0.0.0.0Nacos 的配置文件application.properties中如果server.address0.0.0.0意味着它监听所有网络接口。即使安全组限制严格服务本身也在等待所有来源的连接。✅更安全的做法将server.address改为127.0.0.1让 Nacos 只监听本地然后通过 SSH 隧道访问。 安全组界面的正确填写字段正确填写错误填写访问来源你的公网IP/320.0.0.0/0或172.16.0.0/12访问目的端口8848/8848选错端口172.16.0.0/12是阿里云内网网段你的家用电脑不在内网中选了这个反而连不上。八、动态 IP 的终极解决方案自动化白名单工具家庭宽带 IP 会随时变化重拨号或运营商重新分配每次手动去安全组改 IP 非常痛苦。这里推荐一个开源工具AliCloud_Whitelist实现 IP 变化时自动更新安全组规则。 准备工作创建 RAM 子账号登录阿里云 RAM 控制台创建用户务必勾选“OpenAPI 调用访问”。保存生成的AccessKey ID和AccessKey Secret。授予该用户AliyunECSFullAccess权限或自定义更精细的策略。⚙️ 安装与配置1. 下载工具在 ECS 服务器上执行sudo wget https://github.com/WillemCode/AliCloud_Whitelist/releases/v1.0.0/download/aliyun-whitelist-linux-amd64 -O /usr/local/bin/aliyun-whitelist sudo chmod x /usr/local/bin/aliyun-whitelist2. 创建配置文件在/root/目录下创建config.yamlvim /root/config.yaml粘贴以下内容替换成你的实际信息aliyun_accounts: - name: SSH管理端口 regionId: cn-hangzhou access_key: 你的AccessKey ID access_secret: 你的AccessKey Secret policy: accept Port_Range: 22/22 Ip_Protocol: tcp Security_GroupId: sg-xxxxxx3. 手动运行测试cd /root ./aliyun-whitelist运行后程序会自动检测当前公网 IP 并添加到安全组中同时生成ipAddr.txt记录当前 IP。4. 设置定时任务crontab -e # 添加每小时执行一次 0 * * * * /usr/local/bin/aliyun-whitelist /var/log/whitelist.log 21 多端口统一管理配置如果入方向有多个端口需要管理在config.yaml中配置多条记录即可aliyun_accounts: # SSH 远程管理 - name: SSH管理端口 regionId: cn-hangzhou access_key: 你的AccessKey ID access_secret: 你的AccessKey Secret policy: accept Port_Range: 22/22 Ip_Protocol: tcp Security_GroupId: sg-xxxxxx # MySQL 数据库 - name: MySQL数据库端口 regionId: cn-hangzhou access_key: 你的AccessKey ID access_secret: 你的AccessKey Secret policy: accept Port_Range: 3307/3307 Ip_Protocol: tcp Security_GroupId: sg-xxxxxx # Nacos 服务 - name: Nacos服务端口 regionId: cn-hangzhou access_key: 你的AccessKey ID access_secret: 你的AccessKey Secret policy: accept Port_Range: 8848/8848 Ip_Protocol: tcp Security_GroupId: sg-xxxxxx # Web 端口范围 - name: Web服务端口 regionId: cn-hangzhou access_key: 你的AccessKey ID access_secret: 你的AccessKey Secret policy: accept Port_Range: 80/443 Ip_Protocol: tcp Security_GroupId: sg-xxxxxx配置完成后程序每次运行时会同时更新所有端口规则IP 变化时全自动同步。 其他备选方案方案优点缺点AliCloud_Whitelist轻量、配置简单、开源需要安装 Go 二进制文件阿里云 CLI Shell 脚本完全可控、无额外依赖需要自己写脚本和调试DDNS 方案域名固定安全组不支持域名无法直接使用VPN / SSH 隧道最安全需要额外配置已在本方案中采用针对你的场景因为数据库和 Nacos 都走 SSH 隧道实际上真正需要安全组白名单的可能只有 SSH22端口和公网 Web80/443。MySQL 和 Nacos 的端口根本不需要出现在安全组规则中。九、安全加固最终检查清单☑安全组没有0.0.0.0/0访问数据库端口☑安全组已限制 22 端口为特定 IP☑80/443 端口保持公开如有网站服务☑MySQL root 密码为 20 位以上强密码☑容器端口映射到127.0.0.1而非0.0.0.0☑应用使用独立的非 root 账号☑启用了阿里云自动快照策略保留 7 天☑Navicat 使用 SSH 隧道连接不暴露数据库端口☑已创建%或172.17.0.1账号解决 Docker 网桥连接问题☑旧容器和残留数据已彻底清理☑SSH 标签页与常规标签页的密码已正确区分☑Nacos 等服务建议改为127.0.0.1监听走 SSH 隧道访问☑安全组规则已清理冗余的0.0.0.0/0规则新规则不会覆盖旧规则☑已配置自动化白名单工具解决动态 IP 问题十、排错经验总结阶段报错现象根本原因解决方案容器创建address already in use3306 端口被占用改用 3307 端口容器创建container name already in use残留容器未删除docker rm -fSSH 隧道Access denied for passwordSSH 标签页填了 MySQL 密码填入 Linux 服务器密码MySQL 权限Host 172.17.0.1 is not allowed账号未授权该来源 IP创建%或172.17.0.1MySQL 权限Access denied for user密码不匹配ALTER USER重置密码安全组配置了白名单但仍被公网访问0.0.0.0/0旧规则仍存在规则叠加手动删除旧规则Nacos只能本地访问但想公网访问服务监听127.0.0.1改为0.0.0.0并配合安全组白名单十一、总结与反思这次事件的教训教训说明密码强度不是“建议”是“强制”弱密码等于没有密码端口暴露比想象中更危险全网扫描每时每刻都在发生备份是最后的尊严没有备份数据丢了就是真丢了安全组 容器配置要双重检查Docker 端口映射和安全组规则缺一不可SSH 隧道是数据库连接的最佳实践比公网直连安全百倍密码要区分清楚Linux 密码 ≠ MySQL 密码安全组规则是“叠加”不是“覆盖”删除旧规则才能让新规则生效动态 IP 要用自动化工具管理手动改安全组不是长久之计一句话总结安全加固不是“锦上添花”而是“生存底线”。这次虽然没有恢复数据因为明确不需要恢复但通过从零到一的完整重建、排错和安全加固这台服务器的安全等级已经大幅提升。从安全组到容器配置从账号权限到自动化运维每一步都积累了宝贵的实战经验。 本文记录了从删库到安全加固的完整实战过程所有命令已在阿里云 ECS Docker 环境中验证。如果你也有类似经历欢迎在评论区交流。