Redis不解析查询参数,穿透问题源于业务层未校验用户输入;需白名单校验键名、空值缓存+布隆过滤器防穿透、禁用高危命令并监控异常行为。

Redis本身不解析查询参数,穿透问题出在业务层
Redis 是一个键值存储,它不理解“参数”“查询”这些概念——GET、HGET、LRANGE 等命令只认键名和固定数量的参数。所谓“恶意构造无效查询参数导致穿透”,实际是上层应用(比如用 redis-py 或 Node.js 的 ioredis)把用户输入直接拼进命令,没校验、没过滤,结果查了不存在的键,又没设缓存,流量直冲下游数据库。
关键点在于:Redis 不会“被穿透”,它只是忠实地执行你发过去的命令;真正被穿透的是你的缓存策略和业务逻辑。
用户输入进 Redis 命令前必须做白名单校验
常见错误是用用户传来的 user_id 直接拼成 user:profile:{user_id},但没检查 user_id 是否为数字、长度是否合理、是否含冒号或空格等非法字符。一旦攻击者传 user_id=123; DROP TABLE users;(虽然 Redis 不执行 SQL,但若后续拼到 SQL 里就完了),或者传超长字符串触发 OOM,风险就来了。
- 对所有用户输入的键名片段,用正则严格限制:只允许字母、数字、下划线、短横线,且长度 ≤ 64 字符(
^[a-zA-Z0-9_-]{1,64}$) - 禁止拼接任意用户输入到键名中;优先用哈希后缀(如
user:profile:+hashlib.md5(user_id.encode()).hexdigest()[:16]) - 若必须保留可读键名(如
order:20240517123456),则对时间戳+订单号做格式校验,拒绝非数字、位数不符、日期明显异常的输入
空值缓存 + 布隆过滤器防穿透要配对用
单纯设空值缓存(SET user:profile:999999 "" EX 60)能挡住重复请求,但挡不住海量随机 key 扫描(比如 user:profile:1 到 user:profile:9999999)。这时候布隆过滤器(Bloom Filter)才起作用——它提前告诉你“这个 key 绝对不存在”,不用查 Redis 也不查 DB。
- 布隆过滤器必须由写入方维护:每次成功写入一个有效
user:profile:{id},就往过滤器里add(id) - 读取前先
contains(user_id),返回false就直接返回 404,不碰 Redis - 不要用 Redis 自带的
BF.RESERVE做线上主过滤器——它的误判率不可控,且扩容麻烦;推荐用客户端侧轻量实现(如 Python 的pybloom_live)或独立服务(如 Bitcask + Bloom) - 空值缓存 TTL 要比正常缓存短(比如 2 分钟 vs 30 分钟),避免脏数据长期滞留
用 Redis 的 RESP 协议特性识别异常命令模式
有些穿透不是来自业务逻辑漏洞,而是攻击者绕过应用层,直连 Redis 发畸形命令,比如超长 key、嵌套过深的 EVAL 脚本、大量 KEYS * 扫描。Redis 本身虽不鉴权,但可通过配置和监控拦住一部分。
- 禁用高危命令:在
redis.conf中加rename-command KEYS ""、rename-command FLUSHDB ""、rename-command EVAL "" - 限制单个命令最大参数个数:通过
maxmemory和maxmemory-policy防 OOM,但更直接的是用代理层(如 Twemproxy 或 redis-exporter + Prometheus alert)检测command_calls_total{cmd=~"get|hget|lrange"} > 10000这类突增 - 开启慢日志:
slowlog-log-slower-than 10000(单位微秒),定期抓取SLOWLOG GET 10,看是否有GET命令耗时异常——往往意味着 key 不存在且后端 DB 查询卡住了
最易被忽略的一点:布隆过滤器的初始化和热更新必须和业务写操作强一致。如果新增用户后忘了 add 到过滤器,就会误判为“不存在”,造成真实用户也 404。这事没法靠 Redis 自动兜底,得靠写操作事务里的回调或消息队列保最终一致。

















