攻击 = 允许被网络访问 + 允许执行危险操作 + 操作落在危险位置。那防御就是三件事:
网络层:不让他连得上(bind、防火墙、不要裸奔公网)。
认证与授权层:就算连上,也要密码 / ACL 挡住危险命令。
系统层:就算命令执行了,低权限用户 + 最小目录写权限,让他写不了能被执行的文件。
1. 先看现状:现在的 Redis 有多裸?
任何加固的第一步是盘点现状。管理员先自查:
# 1) 谁在监听、监听在哪张网卡
ss -tlnp | grep 6379 # 看是否 0.0.0.0 / *(公网)在听
# 或
netstat -an | grep 6379
# 2) 关键配置现状
redis-cli CONFIG GET bind
redis-cli CONFIG GET protected-mode
redis-cli CONFIG GET requirepass
redis-cli CONFIG GET dir
redis-cli INFO server | grep redis_version
# 3) 当前用户/权限
ps -ef | grep redis-server | grep -v grep # redis 以什么用户跑?
我怎么知道 Redis 是否暴露在公网?
上面 ss 看监听地址:0.0.0.0:6379 或 *:6379 = 所有网卡都在听(含公网)。也可以从另一台机器 telnet 公网IP 6379 试连通性。安全工具如 shodan/fofa 能直接搜到全网暴露的 6379——那些基本都是被攻击重灾区。
2. 网络层加固:让攻击者"连不上"
2.1 bind:只监听需要的网卡
# redis.conf 里
# 只允许本机访问(最安全,开发/单机推荐)
bind 127.0.0.1
# 只允许内网特定 IP 访问(生产常用,按需改)
bind 127.0.0.1 10.10.10.5
# 千万别这样(监听所有网卡,等于暴露公网)
# bind 0.0.0.0
# 或注释掉 bind
不写 bind 0.0.0.0 时,老版本默认可能监听所有网卡——所以显式写明 bind 是好习惯。
2.2 protected-mode:兜底开关,务必保持 yes
protected-mode yes
protected-mode 只在"没设密码 + 非本机连接"时才拦。它是兜底不是万能。正确姿势:bind 收紧 + 设密码 + protected-mode yes 三者一起上,别只靠一个。
2.3 防火墙 / 安全组
即使 bind 失误,网络层还有最后一道闸:
# 只允许信任网段访问 6379
iptables -A INPUT -p tcp –dport 6379 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp –dport 6379 -j DROP
# 云上:安全组入方向不向 0.0.0.0/0 放行 6379
3. 认证与授权:连上了也要拦
3.1 requirepass:设置访问密码(最基础)
Redis 6 之前只有这一种认证方式,所有客户端共用一个密码。
# redis.conf
requirepass 一个足够强、且不重复使用的密码
验证:
redis-cli -p 6379 ping
# (error) NOAUTH Authentication required. ← 没密码进不去,攻击断一条腿
redis-cli -p 6379 -a 密码 ping
# PONG
设了 requirepass 之后,主从/哨兵之间也要配密码(masterauth),否则从库连不上主库——这属于运维细节,但漏配会导致复制失败告警。
3.2 ACL:细粒度控制"谁能执行哪些命令"(Redis 6+,强烈推荐)
requirepass 是"一刀切",ACL 才是现代 Redis 的正解:可以建多个用户,每个用户指定能访问哪些 key、执行哪些命令。
ACL 基本模型:
ACL SETUSER 用户名 规则…
常用规则:
| on / off | 启用 / 停用该用户 |
| >密码 | 设置密码 |
| ~pattern | 允许访问匹配的 key(~* 所有;~cache:* 只 cache 前缀) |
| +命令 / -命令 | 允许 / 禁止某命令 |
| +@类别 / -@类别 | 按类别批量放行/禁止(如 +@read -@admin) |
| allkeys / allcommands | 等价 ~* / +@all |
| nopass | 免密(default 用户默认 nopass,危险) |
创建一个只能读 cached:*、只能执行少量命令的只读用户(演示,真实按业务改):
# 建用户:demo,密码 s3cretPwd,只能访问 cached:*,只允许 ping/get/set
redis-cli -p 6379 ACL SETUSER demo on '>s3cretPwd' '~cached:*' '+ping' '+get' '+set'
# 查看所有用户
redis-cli -p 6379 ACL LIST
# 输出示例:
# user default on nopass sanitize-payload ~* &* +@all ← 默认超级用户
# user demo on sanitize-payload #hash… ~cached:* resetchannels -@all +get +set +ping
用 demo 用户连:
redis-cli -p 6379 –user demo -a s3cretPwd
# 能 get cached:xxx、set cached:yyy、ping
# 但不能 KEYS、不能 FLUSHALL、不能 CONFIG → 会被 (error) NOPERM 拒绝
ACL 和 requirepass 能同时用吗?
能。requirepass 本质是给 default 用户设密码。Redis 6+ 更推荐直接用 ACL 管理用户,可以保留 default 强密码,另建只读/最小权限用户给不同业务方。
为什么 ACL 能防住攻击?
前面所有 getshell,最后都依赖 CONFIG / BGSAVE / REPLICAOF / MODULE / EVAL 这类高危命令。用 ACL 把业务账号限制成只读 + 只碰业务 key,攻击者拿到这个账号也调不动任何危险命令。
3.3 禁用/改名高危命令(无 ACL 的版本也能用)
老版本没有 ACL,用 rename-command 把危险命令改名或直接禁用(只能写在 redis.conf 里,重启生效,CONFIG SET 改不了):
# redis.conf
# 把危险命令禁掉(改名成空字符串 = 禁用)
rename-command CONFIG ""
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command EVAL ""
rename-command SCRIPT ""
rename-command SHUTDOWN ""
# 或改成只有你知道的暗名(如果业务确实要用)
rename-command CONFIG "r3dis_c0nfig_9527"
改名命令的坑:
主从、哨兵、客户端里如果写了 CONFIG,改名后全都要跟着改,否则复制/运维脚本会挂;
rename-command 只在配置里生效,无法用 CONFIG SET 在线改;
改名前先在测试环境验证业务兼容性。
4. 系统层:就算命令执行了,也"写不了 / 干不了活"
4.1 用低权限用户跑 Redis
这是最容易被忽略、也最有效的一条。 Redis 若以 root 跑,写 crontab / SSH 公钥 / WebShell 全都有权限;用普通用户跑,那些系统目录根本写不进去。
# 建一个无登录、最小权限的 redis 用户
useradd -r -s /sbin/nologin redis
mkdir -p /var/lib/redis /var/log/redis /etc/redis
chown redis:redis /var/lib/redis /var/log/redis /etc/redis
# redis.conf 里指定
user redis
# 或 systemd 单元里 User=redis
为什么有些未授权 Redis 打不出 getshell?
很多时候不是命令被禁,而是 Redis 跑在 redis 用户下,/root/.ssh、/var/spool/cron、web 目录它都没权限写——这正是低权限运行的防御价值。
4.2 数据目录与落盘位置最小化
-
给 Redis 一个独立且无执行权限的数据目录(如 /var/lib/redis),不要把 dir 指到 web 根目录。
-
即使攻击者改了 dir,目录权限 + SELinux/AppArmor 也能二次拦截。
-
定期检查 dir 里有没有异常文件(攻击者落盘的 shell.php 之类)。
4.3 日志与监控
-
开 Redis 日志(logfile),记录连接和命令。
-
监控 6379 的连接来源、异常 KEYS * / CONFIG SET / FLUSHALL / REPLICAOF 命令。
-
对外部发起的长时间连接、异常大流量保持警惕。
5. 云上/容器部署的额外注意
□ Docker:端口映射别裸开 6379(-p 6379:6379 且无密码 = 灾难);用 –network 内网或仅本机
□ k8s:Redis 服务不要暴露成 NodePort/LoadBalancer 到公网
□ 云托管 Redis(如云厂商):用安全组/VPC 隔离 + 开密码 + 开审计
□ 公网示例默认账号/端口:第一时间改端口或至少加密码,别用默认 6379 裸奔
6. 最小加固清单
按顺序做,前三条性价比最高:
# ===== redis.conf 最小安全配置 =====
bind 127.0.0.1 # 1. 不对外监听(或用内网IP)
protected-mode yes # 2. 保护模式兜底
requirepass 换成超强随机密码 # 3. 无密码 = 未授权访问(高危)
# 4. 若有 ACL,给业务用最小权限用户(可选但推荐)
# 5. 禁用/改名危险命令(老版本)
rename-command CONFIG ""
rename-command FLUSHALL ""
# 6. 系统层
# 用非 root 用户跑 redis
# 数据目录独立且不可写危险位置
# 7. 网络层
# 防火墙/安全组只放行信任来源
即使只做 1+2+3,之前的四条 getshell 全都会断在第 1 步(连不上/要密码)。再做 5,连"拿到密码但想用危险命令"也被断。这就是分层防御:每一层都独立地挡住一种攻击路径。
7. 攻防对照表
| 未授权访问(03) | bind 0.0.0.0 + 无密码 | bind 收紧 + protected-mode + requirepass/ACL |
| 爆破弱口令 | 弱密码 | 强密码 + 限制失败次数/来源 IP + 日志监控 |
| 写 crontab/公钥/WebShell(03) | CONFIG SET dir + 高权限 + 落盘 | 禁用 CONFIG + 非 root 跑 + 目录权限/SELinux |
| 主从复刻 RCE(03) | REPLICAOF + 模块加载 + 可反连 | 禁用/改名 REPLICAOF + 版本升级 + ACL + 出网限制 |
| SSRF 打 Redis(04) | 内网可达 + 无密码 | 网络隔离 + 密码 + SSRF 点本身修复(你 SSRF 篇已讲) |
| 信息泄露(读业务数据) | 可连接可读 | ACL 最小权限 + 敏感数据加密存储/不要明文进 Redis |
| DoS(KEYS */灌数据) | 单线程 + 可执行 | 禁 KEYS/FLUSHALL + 限内存 maxmemory + 连接数限制 |
8. 失陷后的排查要点
如果怀疑 Redis 失陷,冷静按下面查(配合应急响应流程):
看有没有被写文件:检查 CONFIG GET dir 当前值、/var/spool/cron*、/root/.ssh/authorized_keys、web 根目录的陌生 .php/.so。
看有没有被改配置:CONFIG GET requirepass、ACL LIST(是否多了陌生用户)。
看有没有被加载模块:MODULE LIST(Redis 4+)。
看主从关系:INFO replication 是否被改成了某陌生主的从库。
看日志:redis.log、系统登录日志、反弹连接的连接记录。
紧急处置:断网隔离 → 改密码/重建实例 → 取证 → 复盘加固。
处置时优先"保留现场取证",别急着 flushall(会把攻击痕迹也清了)。
9. 小结
防御的本质不是背命令,而是分层:
网络层(连不上) → 认证层(连上也要密码) → 授权层(危险命令被禁)
→ 系统层(命令执行了也写不了危险位置) → 监控审计(打进来能发现)
攻防其实是同一张地图,攻方从外往内一层层找"允许",守方从内往外一层层设"不允许"。
全系列自测
用自己的话,把"未授权 Redis 为什么危险"从网络→认证→写文件→执行,完整讲一遍因果链。
给一个"只能跑缓存、永远不该被当数据库写"的 Redis 写一份最小安全配置。
假设你是蓝队,收到"内网 6379 裸奔"告警,写出你从确认到处置的完整动作清单。
说出 ACL 里限制一个业务账号"只能读某前缀 key、不能用 CONFIG/FLUSHALL/EVAL"的完整命令。
解释为什么"Redis 用 root 跑"比"没设密码"在某些场景更致命。




