ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Redis与MySQL弱口令及未授权访问排查与加固实战指南

Redis与MySQL弱口令及未授权访问排查与加固实战指南 1. 为什么 Redis 和 MySQL 是弱口令与未授权访问的高发区先说个扎心的事实我做过不少次内部安全巡检十套业务系统里至少有两三套数据库是“裸奔”状态。这里的“裸奔”指的不是业务裸奔而是数据库端口直接暴露在公网或大内网配上默认端口、空密码、弱口令基本等于把数据仓库的钥匙挂在门口。Redis 和 MySQL 又是其中最典型的重灾区原因很简单它们装起来太容易、用起来太顺手很多团队默认配置一把梭装完就忘了还有安全这回事。1.1 弱口令和未授权访问到底是怎样一个漏洞先把这个概念拆清楚。弱口令很好理解就是密码强度不够常见的有 admin/123456、root/root、redis/redis 这类。未授权访问则是指服务端没有做身份验证或者验证逻辑存在缺陷导致攻击者不需要任何口令就能直接连上服务拿到读写权限。拿 Redis 来说默认情况下它绑定在 0.0.0.0:6379开启了 protected-mode但如果没配置 requirepass 密码或者 protected-mode 因为某些原因失效攻击者用redis-cli -h 目标IP -p 6379直接 ping 一下收到 PONG那这台 Redis 就等于被人打开了大门。更麻烦的是 Redis 的配置文件和数据都可以通过命令修改攻击者还能写入一些恶意数据配合 Web 应用的解析逻辑甚至能进一步拿到服务器权限。MySQL 的情况稍有不同它默认就要求账号密码登录问题主要集中在弱口令、空口令、默认账号没有及时清理、账号权限过大这三个方面。root 账号允许任意主机登录、密码还是 123456 的案例我在项目里见过不止一次。攻击者只要跑一遍密码字典几秒钟就能撞开大门之后就是拖库、删库、勒索三件套。这里需要说清楚一个概念弱口令和未授权访问是两种不同的漏洞类型但经常一起出现。Redis 的未授权访问往往是因为根本没有密码或密码为空MySQL 的弱口令则是因为密码太简单或长期不更换。最后造成的后果是一样的——数据库数据被读取、被篡改、被删除业务系统被入侵严重的话连主机都会被控制。1.2 内网业务系统最容易踩坑的三种姿势我总结了一下实际项目里最容易出问题的不是那些复杂场景反而是最基础的三种配置姿势。第一种安装完保持默认配置。Redis 下载解压后是开箱即用的默认配置里没有密码、没有绑定限制如果直接启动就暴露在网络上那未授权访问几乎是一定的。很多教程只教你redis-server启动没告诉你还要改配置。第二种只改了 bind 没改端口或者只改端口没设密码。有些同事知道 Redis 不安全就把端口从 6379 改成 16379以为改个端口就安全了。这种想法是典型的安全误区——端口扫描是基础操作改端口顶多增加一点时间成本不能代替真正的安全配置。第三种MySQL 空口令 全权限。装完 MySQL用 root 空密码或者 123456 登录然后直接拿 root 账号给业务系统用。业务代码里数据库连接串写得清清楚楚一旦被泄露或被扫描到攻击者直接拿这套账号密码去登生产库权限还是超级管理员级别。这种问题比 Redis 未授权访问更隐蔽因为它表面上“有密码”实际等于没有。这三种姿势看起来很低级但在真实项目中真的非常常见。尤其是开发环境和测试环境的数据库很多人觉得“反正是内网不会被扫到”结果内网被横向渗透后数据库就成了下一个目标。2. 漏洞自查先知道自己的数据库到底暴露了什么排查漏洞之前你得先知道自己现在是什么状态。这一节全部用实际操作说话环境是 Linux Redis 6.x MySQL 8.xWindows 下也有对应的客户端工具思路一样。2.1 Redis 未授权访问自查操作第一步确认 Redis 进程是不是真的在监听外部地址。在服务器上执行ss -tlnp | grep 6379如果输出是0.0.0.0:6379或者*:6379说明它监听在所有网络接口上不只是本地回环。如果看到的是127.0.0.1:6379那么外部默认是连不上的相对安全但这只代表端口层面。第二步用 Redis 客户端直接连上去测试。你可以从本机连也可以从另一台机器连。假设服务器 IP 是 192.168.1.100redis-cli -h 192.168.1.100 -p 6379进去之后执行PING如果返回PONG说明不需要认证就能访问。再执行INFO CONFIG GET dir如果这些命令都能正常返回那就不只是未授权访问了还是可以直接读取服务器配置的高危状态。只要能看到dir或dbfilename说明攻击者可以把数据库文件写到任意位置后续利用空间非常大。第三步查看 Redis 配置文件确认安全项。找到 redis.conf重点看这几处grep -E ^bind|^protected-mode|^requirepass|^port|^rename-command /etc/redis/redis.conf如果 bind 是 0.0.0.0 或直接被注释掉protected-mode 是 norequirepass 是空的那么恭喜你这台 Redis 在互联网上基本属于谁都能用的状态。即使 protected-mode 是 yes只要 bind 绑定了公网 IP 且没有密码仍然存在被爆破的风险。除了手工命令也可以借助端口扫描工具做外部视角的验证比如用 nmap 从另一台机器扫描目标 6379 端口是否开放开放了再用--script redis-info看能否获取 Redis 版本和基本信息。这个测试建议在自己拥有或得到授权的环境中做不要对别人的服务器乱扫。2.2 MySQL 弱口令与空口令自查操作先看本机 MySQL 的账号权限情况。用 root 或其他有权限的账号登录mysql -uroot -p然后查询用户表SELECT user, host, authentication_string, plugin FROM mysql.user;重点关注三个地方一是 host 为%的账号表示允许任意 IP 登录二是 authentication_string 为空或者加密串过于简单比如常见弱密码的 hash三是 user 为 root 但 host 是%的情况。接着测试实际登录是否能用弱口令。从远程机器上尝试mysql -h 192.168.1.100 -uroot -p123456 mysql -h 192.168.1.100 -uroot -p如果第一条能连上弱口令实锤如果第二条空密码能连上那问题更严重等于完全没设密码。注意MySQL 8.x 默认使用 caching_sha2_password 插件空密码登录通常会直接拒绝但 5.x 老版本有可能存在空密码账号。再检查远程访问权限是否被放得过宽。执行SHOW VARIABLES LIKE bind_address;如果输出是*或0.0.0.0说明 MySQL 接受所有网络接口的连接。再配合防火墙查看 3306 端口是否对公网开放就能判断这台数据库是不是已经暴露出去了。2.3 用配置文件与日志交叉确认风险面命令查到的结果只能说明“当前运行状态”配置文件则能看出“意图”。有时候你改了配置没重启或者重启后配置被覆盖实际运行状态和文件内容会出现偏差。所以两边都要看互相印证。Redis 的日志默认在 console 输出如果启动了 logfile 配置可以在日志里看到有没有陌生 IP 连接过。MySQL 的通用日志默认关闭但错误日志里会记录登录失败的信息。临时开启通用日志来做排查是可以的但记得排查完关掉否则日志量会非常惊人SET GLOBAL general_log ON; SET GLOBAL general_log_file /tmp/mysql_general.log;然后过一段时间查看日志里的连接来源和执行的 SQL。这个操作在确认是否已被入侵时非常有用可以看到攻击者执行了哪些命令、读取了哪些数据。用完之后务必关闭SET GLOBAL general_log OFF;日志排查的意义在于即使你现在还没被攻击通过日志可以了解你的数据库在网络上被多少扫描器“问候”过。我在实际操作中看到过很多次一个暴露在公网的 MySQL 端口一天之内会被来自不同 IP 的自动扫描脚本尝试登录几百次。等到真出事的时候再看日志往往已经晚了。3. 加固落地从端口、权限、口令三个维度堵住风险排查完问题之后紧接着就是动手加固。这一节我把配置项拆开讲先说明原理再给具体操作保证你能看懂为什么这样配。3.1 Redis 加固监听地址、密码、危险命令三件套Redis 的核心加固思路就三条不让外部访问、访问必须认证、危险命令直接禁用。第一步限制监听地址。把 bind 改成内网专用地址或回环地址。如果你的 Redis 只给本机应用用那就 bind 127.0.0.1如果其他服务器也要连就 bind 内网 IP不要 bind 0.0.0.0。bind 127.0.0.1 192.168.1.100 protected-mode yes port 6379这里说明一下为什么 bind 多个地址是更安全的做法。当你 bind 只写内网 IP 时公网接口上即使 6379 端口没被防火墙挡住网络层也不会接受这个连接等于加了一道网络隔离。比单纯依赖防火墙更可靠。第二步设置强密码。在 redis.conf 中requirepass 这里写一个足够复杂的密码密码生成可以用系统命令openssl rand -base64 32设置完密码后客户端连接需要加-a参数或者在登录后执行AUTH命令。注意-a参数在命令行里会留在 shell 历史中服务器上如果有别人能看到历史记录建议用REDISCLI_AUTH环境变量代替。第三步禁用危险命令。Redis 的CONFIG、FLUSHALL、FLUSHDB、EVAL等命令权限太大一旦被未授权访问后果非常严重。在 redis.conf 末尾追加rename-command CONFIG rename-command FLUSHALL rename-command FLUSHDB rename-command EVAL 这里的原则是“按需启用”。如果业务确实需要EVAL执行 Lua 脚本可以考虑保留但限制来源或者改成一个不易被猜到的命令名比如rename-command EVAL eval_9f8a1b2c。改名不是为了隐藏而是为了增加攻击者的利用成本。3.2 MySQL 加固口令策略、账号权限、访问来源三件事MySQL 加固的核心不是把 root 密码改复杂就完事了三分靠密码七分靠权限设计。第一件事检查并清理默认账号和空口令账号。执行DELETE FROM mysql.user WHERE user; DELETE FROM mysql.user WHERE authentication_string AND pluginmysql_native_password; FLUSH PRIVILEGES;注意清理账号前建议先确认没有应用在用这些账号否则会影响业务连接。第二件事调整 root 账号的远程登录限制。如果业务不需要 root 远程连那把 root 的 host 改成 localhostRENAME USER root% TO rootlocalhost; FLUSH PRIVILEGES;如果 root 远程登录确实需要建议改成一个专用的只读账号或最小权限账号不要让 root 在外部网络里出现。第三件事为业务创建最小权限账号并限制来源 IP。比如一个名为 app 的业务需要访问 appdb 数据库CREATE USER app192.168.1.% IDENTIFIED BY 强密码; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO app192.168.1.%; FLUSH PRIVILEGES;192.168.1.%表示只允许这个网段的服务器连接其他 IP 一律拒绝。权限只给 SELECT、INSERT、UPDATE、DELETE不给 DROP、ALTER、GRANT。口令策略层面MySQL 8.x 自带 validate_password 组件可以启用mysql -uroot -p INSTALL COMPONENT file://component_validate_password; SET GLOBAL validate_password.policy MEDIUM; SET GLOBAL validate_password.length 8;这样之后创建用户时密码长度和复杂度就会受到约束避免业务同学随手写个 123456 当密码。3.3 防火墙与主机层面的兜底策略数据库加固做完后防火墙是最重要的兜底措施。即使配置再完美一个直通公网的 3306 端口仍然是巨大的攻击面。原则是数据库端口只对需要的来源开放公网 IP 一律不放行。在 CentOS/RHEL 系使用 firewalldfirewall-cmd --permanent --remove-servicemysql firewall-cmd --permanent --remove-port6379/tcp firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.0/24 port port3306 protocoltcp accept firewall-cmd --reload在 Ubuntu/Debian 系使用 ufwufw deny 3306/tcp ufw deny 6379/tcp ufw allow from 192.168.1.0/24 to any port 3306 proto tcp ufw reload如果你是云服务器云平台的安全组同样要配置白名单规则。这里有个非常关键的细节云安全组和服务器本地防火墙是两层独立的防线任何一层没堵住都不算安全。我遇到过的情况是本地开了 iptables 但云安全组放行了 0.0.0.0/0结果端口照样暴露。所以两层都要查。还有主机层面的一个容易忽略的坑——Redis 和 MySQL 进程的运行用户。永远不要用 root 跑这两个服务否则一旦被拿权限攻击者直接获得 root 级别的控制能力。建议创建专用账号运行服务相关目录权限收紧到该账号。3.4 加固脚本参考安全可靠为了便于落地我写了一个加固脚本片段整合了 Redis 的主要加固点。根据你的实际环境修改后使用#!/bin/bash # Redis 加固脚本适用 Redis 6.x运行前请确认路径 REDIS_CONF${1:-/etc/redis/redis.conf} # 生成随机密码并写入配置 PASS$(openssl rand -base64 32) sed -i s/^# requirepass.*/requirepass ${PASS}/ $REDIS_CONF sed -i s/^requirepass.*/requirepass ${PASS}/ $REDIS_CONF # 绑定内网地址避免监听全部网卡 sed -i s/^bind .*/bind 127.0.0.1 内网IP/ $REDIS_CONF # 开启 protected-mode sed -i s/^protected-mode no/protected-mode yes/ $REDIS_CONF # 重命名危险命令 cat $REDIS_CONF EOF rename-command CONFIG rename-command FLUSHALL rename-command FLUSHDB EOF redis-server $REDIS_CONF --daemonize yes echo Redis 加固完成密码已生成$PASS echo 提示请立即将密码保存到密码管理器中该密码只显示这一次脚本不是万能的它只处理了最常见的几个加固点。真正负责的态度是每次配置修改后都重启服务并重新验证一遍确认业务连接没有受影响。4. 常见问题与排查技巧实录这一节我把这些年实打实踩过的坑整理出来按出现频率排序基本都是新手甚至老手都容易翻车的地方。4.1 我踩过的坑为什么配置了 requirepass 还是被扫到我见过最多的问题是这种明明 redis.conf 里写了 requirepass重启后redis-cli还需要密码才能登录但用 nmap 扫描还是能发现 Redis 服务甚至某些扫描工具显示“未授权访问”。开始我也觉得奇怪后来一步步排查发现是配置被覆盖了。当时场景是这样的Server 上有两个 Redis 实例一个端口 6379一个端口 6380。加固的时候我改了 6379 的配置文件但启动脚本里启动命令写的是redis-server /etc/redis/redis_6379.conf。看起来没问题但系统里同时存在一个/etc/redis/redis.conf和一个/etc/redis/redis_6379.conf启动脚本引用的是后者而我改的是前者。等于我忙活了半天改了个没被加载的文件。这个坑的关键是改完配置文件后确认实际加载的是哪个文件。方法很简单redis-cli -h 127.0.0.1 -p 6379 CONFIG GET dir CONFIG GET requirepass CONFIG GET bind如果 CONFIG 返回的 requirepass 是空字符串说明你改的文件根本不是它加载的文件。找到真正加载的配置再改改完重启然后再用 CONFIG 确认。第二个坑发生在 MySQL 上。当时有个项目DBA 建了一个远程账号密码设得非常复杂但账号的 host 写的是%。我建议收紧到业务服务器的具体 IP结果程序连不上了。排查之后发现应用的数据库连接串偶尔会走 DNS 轮询多个出口 IP 都会进来限制了 host 反而导致连接不稳定。这种情况合适做法是限制到网段比如192.168.2.%而不是具体到某一台 IP。第三个坑是关于 Redis 密码的。有同事为了图方便直接写了个简单密码还在命令行里用redis-cli -a 123456测试结果密码被记录到了 shell 历史里。这个姿势等于主动泄露密码。正确做法是用环境变量export REDISCLI_AUTH复杂密码 redis-cli -h 127.0.0.1 -p 6379这样密码不会出现在命令行参数里也就不会留到 history 中。4.2 自查命令速查表为了方便现场排查我把几组关键命令整理成了速查表直接复制就能用检查项命令正常结果危险结果Redis 监听地址ss -tlnp | grep 6379127.0.0.1:63790.0.0.0:6379或*:6379Redis 未授权访问redis-cli -h IP -p 6379后执行PING需要密码认证返回 PONGRedis 配置密码查看 redis.conf 中 requirepass有复杂密码为空或没有该行MySQL 监听地址SHOW VARIABLES LIKE bind_address127.0.0.1 或内网 IP*或0.0.0.0MySQL 远程账号SELECT user,host FROM mysql.userhost 都不是%存在 host 为%的账号MySQL 弱口令远程执行mysql -h IP -uroot -p123456拒绝连接成功登录端口暴露nmap IP -p 3306,6379filtered 或 closedopen这里的 nmap 扫描建议严格限定在自己负责或有授权的环境内公网上随便扫别人的机器是违规的千万不要拿它去练手。4.3 加固之后怎么验证以及如何持续监控加固不是一次性的工作。配完 Redis 密码、收紧 MySQL 权限之后至少要从两个维度验证。第一个维度是“攻击者视角”。从外部服务器或另一台安全设备上执行一次端口扫描和连接测试确认端口不再对外开放或者即使开放也需要正确的认证信息。第二个维度是“业务视角”。回到各业务应用服务器用业务实际使用的连接串去连数据库确认业务没有受到影响。这一步很多人容易漏掉——安全加固做完了业务也崩了这就不是加固是事故了。持续监控方面我有几个建议一是开启 MySQL 的审计日志或者至少留存错误日志定期检查是否有大量失败登录记录。二是对 Redis 做定期巡检重点检查配置文件哈希是否发生变化。如果没人动过配置但 hash 变了说明有可疑写入要立刻排查。三是用弱口令检测工具定期扫描内网资产池。安全团队可以每个季度做一次全面排查把新增的服务器、新装的数据库及时纳入巡检范围。四是身份认证统一化。有条件的话企业内部尽量统一用堡垒机跳转连接数据库不让人直接通过公网访问 3306 和 6379 端口。堡垒机至少能做到账号统一管理和操作审计。根据我个人经验数据库安全最大的敌人不是攻击者而是侥幸心理。很多人总觉得“我这服务器那么小谁闲着没事打我”但现实是攻击者根本不用盯上你扫描器扫到你就顺手试一下试通就是中奖试不通就换下一个。把密码改复杂、把端口关严实、把权限缩小一些这些基础操作做完你就已经能挡住 99% 的扫描和爆破尝试。剩下的 1% 靠的是持续巡检和日志监控而不是运气。最后再分享一个小技巧每次加固完数据库之后把变更记录写进一个简单的文本文件放到团队的运维知识库里内容包括改了哪个配置、为什么改、影响范围是什么。等下次排查问题的时候这份记录能帮你省下一整个下午的时间。安全这件事做得越细后面越省心。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进