刚入行安全运维那几年,我几乎每天都要和一批“不起眼”的服务打交道,Memcached 排得进前三。它不像数据库那样被谨慎对待,也不像公网 Web 服务那样时刻被人盯着,很多人认为它只是个缓存集群,就算被碰一下也出不了大事。结果呢?从 CVE-2013-7239 到后来的 Memcached UDP 放大攻击,一次一次提醒我们:缓存服务根本不是安全真空地带,反而经常是黑客最喜欢捡漏的目标。
今天这篇,我就以 CVE-2013-7239 为例,把它背后的认证绕过原理、攻击链路、修复方案和自查手段完整拆开讲。如果你正在维护任何带缓存层的系统,或者你只是想搞懂“为什么缓存服务也值得安全投入”,这篇内容应该能帮上忙。我会尽量把“为什么会产生这个漏洞”和“我该怎么落地修复”讲清楚,而不是只贴一堆补丁公告。
1. 为什么一个 2013 年的缓存漏洞现在还要关注
1.1 Memcached 在系统中的特殊位置
先花 30 秒回忆一下 Memcached 是什么。它是一个开源的高性能分布式内存缓存系统,最常见的使用方式是放在数据库前面,把频繁读取的数据先缓存在内存里。比如某个热门商品详情页,数据库可能一秒要扛几千次查询,但大部分请求查到的都是同一批数据,这时候 Memcached 能直接命中返回,数据库压力一下子降下来。
这个位置决定了它的特殊性:它在业务链路中“不上不下”,既不是最核心的数据持久层,也不是用户直接访问的入口。但也正因为如此,很多团队对它的安全态度非常随意。我在实际评估中见过太多部署实例:监听 0.0.0.0、没有任何认证、防火墙规则大开、监控缺失。用大白话说,等于在自己家门口放了个快递柜,谁都能往里塞东西,谁都能把里面的包裹拿走。
CVE-2013-7239 正是在这种“和缓存相关的安全习惯普遍缺失”的背景下出现的。它不是一个配置不当的问题,而是一个代码级别的认证绕过漏洞。换句话说,哪怕管理员已经意识到需要给 Memcached 加认证,只要版本不够新,攻击者依然有办法把这个认证绕过去。
1.2 一个经典的认证绕过:只有缓存不安全
这个漏洞的编号是 CVE-2013-7239,受影响版本主要是 Memcached 1.4.15 及更早版本。它出问题的位置在 SASL 认证处理逻辑里。SASL 全称是 Simple Authentication and Security Layer,简单说就是给 Memcached 提供用户名密码认证能力的一套机制。管理员可以通过启动参数开启它,让客户端必须通过认证之后才能读写数据。
听起来很合理对吧?但问题就在于,Memcached 在解析二进制协议的 SASL 认证数据时,没有正确校验认证字符串的长度和有效终止方式。攻击者可以构造一个特殊的认证请求,让服务端以为认证已经成功,实际上根本没有提供有效的用户名密码。当年不少安全公告用了“空字节字符串绕过认证”这样的描述,比较直观。
我当时看到一个观点非常认同:这就像小区门禁系统只检测你有没有“滴”的一声,但不检测门禁卡里有没有有效权限。如果攻击者知道这个漏洞,他面对的就是一个“看起来上了锁,实则一推就开”的缓存服务。
更要命的是,这个漏洞发生的年代,很多公司的内网架构还没有什么容器隔离、安全组策略,Memcached 通常直接跟随业务服务器分布在直连网段里。一旦某个端口暴露,攻击者根本不需要费劲去“撞库”,只要按漏洞逻辑发送构造包即可。所以它不是那种“理论存在但实践难用”的漏洞,而是真的能被脚本化利用的入口。
2. CVE-2013-7239 的前因后果
2.1 SASL 认证机制是怎么来的
要理解这个漏洞,先要知道 Memcached 认证机制的设计初衷。Memcached 本身最初是一个完全开放的缓存服务,默认情况下不校验任何身份。设计者当时的想法很单纯:缓存层跑在可信内网,性能优先,认证带来的开销能免则免。但后来大家发现,如果不加任何保护,Memcached 暴露在网络上,等于把数据库前面的一层数据直接送给陌生人。于是社区引入了 SASL 支持。
SASL 是一套标准化的认证框架,不只是给 Memcached 用,很多应用都基于它做认证。管理员只要用支持 SASL 的编译版本,并且启动时加上-S参数,Memcached 就会要求客户端在连接后提供认证信息。客户端通过二进制协议发送用户名、密码等认证数据,服务端校验通过后才允许后续操作。
这里就涉及一个二进制协议细节。传统文本协议直接一行一行发命令,但二进制协议为了效率,会把数据以“长度字段 + 实际内容”的方式封装。服务端处理时,应当先读取长度,再按长度读取数据,并对数据内容做合法性校验。
CVE-2013-7239 的根因就在这一步:处理 SASL 认证数据的函数没有对“认证数据长度”以及“认证数据是否正确结束”做完整校验。攻击者发送认证数据时,可以把有效内容长度做成 0,或者让数据中没有正常的字符串终止符。服务端在某些情况下会把这个请求当成合法认证处理,直接放行。
2.2 漏洞本质:长度校验缺失
我现在尝试把技术细节讲得更直观一点。想象你填写一张取件凭证,凭证上有一栏“取件人姓名”,正常的流程是必须填一个非空姓名,然后工作人员核验身份。但这个系统有一个缺陷:当你把“取件人姓名”那一栏留空,系统却默认这个空姓名已经正确匹配了某个有效用户。于是,你拿着几乎什么也没填的凭证,就能把快递取走。
CVE-2013-7239 在代码层面的原理与此非常接近。在process_bin_sasl_auth函数中,Memcached 需要解析客户端发来的 SASL 认证块。攻击者把特定字段置空,让服务端没有做完整校验就直接走入了“认证成功”的逻辑分支。这个漏洞的严重性在于:它针对的是“认证本身”,绕过了最基本的访问控制。
大家可以类比一下:如果服务器上运行的服务根本不设防,那是管理员配置问题;但如果你明明设了密码,密码却可以被空值绕过,那就是代码缺陷。从风险优先级来说,代码缺陷显然更隐蔽、更危险。因为很多管理员在部署时确实会想“我已经开启 -S 参数,总该安全了吧”,但实际上版本不对仍然白搭。
2.3 完整的“黑客利用”链路拆解
我以一个攻击者视角,把这个漏洞被利用的完整链路整理一下,方便你理解怎样的环境会被真正打穿。
整个利用可以分成五个阶段:
- 端口扫描与服务识别:攻击者先在内网或公网扫描 TCP 11211 端口。确认目标开放后,发送
stats或version命令,识别 Memcached 版本。这一步是为了确认是否存在 CVE-2013-7239 对应的旧版本。 - 确认认证模式:攻击者查看服务是否开启了 SASL。有些版本可以通过连接后的响应特征判断;也可以通过对比不同请求的返回值推测。如果服务端给出了“需要认证”的响应,攻击者不仅不会放弃,反而会更加留意,因为这正好是 CVE-2013-7239 的触发场景。
- 构造空认证数据:攻击者用二进制协议构造一个 SASL 认证消息,但把认证数据长度设为 0 或填入不合规的字符串。这一步不需要知道任何用户名和密码。
- 绕过并获取操作权限:服务端因为缺陷把请求判定为认证成功。攻击者随后可以像合法客户端一样执行
get、set、delete、flush_all等命令。 - 扩大战果或影响业务:攻击者读取缓存里的用户会话、订单信息或业务配置,也可以篡改缓存内容,让后续访问这些缓存的用户拿到被污染的数据。
这里要特别强调一下第五步的危害。很多团队认为“缓存里都是些临时热数据,丢了也没什么大不了”。但如果缓存里存的是用户登录态、权限标识、优惠信息、商品价格,那结果可能完全不是“没什么大不了”。攻击者把一个键值改成自己期望的内容,后续大量业务请求会直接读到这段被篡改的数据,影响面可以非常大。
3. 影响范围与实际攻击场景
3.1 不同部署模式下的威胁差异
不是每个 Memcached 实例都会被这个漏洞命中,威胁程度和部署方式强相关。我在下面列一个对照表,你可以直接给自己的环境“对号入座”。
| 部署模式 | 是否启用 SASL | 实际风险 |
|---|---|---|
| 监听 127.0.0.1,仅本机应用访问 | 是 | 风险很低,攻击者难以触达端口 |
| 监听 127.0.0.1,仅本机应用访问 | 否 | 风险较低,但本机其他恶意进程可滥用 |
| 监听内网地址,业务服务跨机访问 | 是 | 存在 CVE-2013-7239 利用风险,取决于版本 |
| 监听内网地址,业务服务跨机访问 | 否 | 高风险,直接未授权访问 |
| 监听 0.0.0.0,公网或大网段可访问 | 是 | 极高风险,CVE-2013-7239 可被远程脚本化利用 |
| 监听 0.0.0.0,公网或大网段可访问 | 否 | 极高风险,等同于裸奔 |
这张表非常直观。最危险的组合是“监听 0.0.0.0 + 老版本 + 开启 SASL”,因为管理员以为自己做了防护,实际上认证可以被绕过。从攻击者的视角看,这比完全开放的服务更有吸引力,因为它具备更高的业务利用价值,更容易被忽视。
我在一次内部评估中也遇到过类似情况。有一个业务模块用了旧版本 Memcached,安全组规则放行了整个办公室出口网段,而服务端也确实加了-S参数。当时很多同事都觉得没问题,毕竟有认证。但当我指出这个 CVE 后,大家才意识到“开启认证”和“认证真的有效”完全是两回事。哪怕那条链路最终未必真的被外部攻击者利用,但风险等级已经完全不同了。
3.2 被绕过认证后,攻击者能得到什么
这个问题几乎每次向业务方提问时,都会出现一段沉默。大多数团队只知道“Memcached 存了热点数据”,但具体热到什么程度、里面有没有敏感信息,往往没人仔细盘点过。
我举几个现实里常见的场景:
- 会话数据:很多老系统会把 session 存在 Memcached 中。攻击者读取到某个用户的 session_id 后,可以伪装登录;如果存在权限相关的标记,攻击者还可以修改标记尝试提权。
- 接口响应缓存:一些团队会把服务端渲染后的页面片段或 JSON 响应放进缓存。攻击者篡改后,后续用户访问页面时看到的内容可能是被注入的广告、钓鱼信息或错误业务数据。
- 业务开关与配置:某些系统会把灰度开关、功能配置也缓存起来。攻击者改了配置键值,等于控制了部分业务逻辑。
- 计数器与限流数据:缓存中保存访问计数、验证码失败次数等数据,攻击者可以清空或重置这些计数器,绕过限流逻辑。
这些场景不一定同时存在于每个环境里,但只要有其中一项,漏洞被利用的后果就不再是“服务重启一下就好”这么简单。它可能直接引发用户信任问题、业务逻辑异常,甚至合规风险。
3.3 和后续 Memcached 安全事件的横向对照
了解一个漏洞最好的方法,是把它放到同类事件中去比较。Memcached 后来还出现过几个非常有名的安全问题,最典型的是 2018 年的 UDP 放大攻击(CVE-2018-1000115)。那个漏洞利用的是 Memcached 默认开启 UDP 11211 端口的问题,攻击者发送小请求,伪造源 IP 并让服务端向目标打出超大流量,形成 DDoS 放大效果。
我列一个简要对照表:
| 事件 | 问题类型 | 主要后果 | 关键缓解手段 |
|---|---|---|---|
| CVE-2013-7239 | SASL 认证绕过 | 未授权访问、数据读取与篡改 | 升级版本、修复认证逻辑、网络隔离 |
| 未授权访问(非 CVE,常见配置问题) | 缺少认证或默认开放 | 任意读写、缓存投毒 | 开启认证、限制监听地址、防火墙 |
| CVE-2018-1000115 | UDP 放大 | 被用于参与大规模 DDoS | 禁用 UDP 端口、升级版本 |
从这个对照可以看出,Memcached 的安全问题并非孤例,而是一个“长期缺乏基础安全设计”的缩影。CVE-2013-7239 解决的是认证绕过,但如果你不解决未授权访问、UDP 开放、监听地址过大等问题,后续仍然可能被其他攻击手法命中。这也是为什么我一直建议团队不要只关注某一个 CVE,而是把它当成一个契机,把 Memcached 的整体安全基线重新梳理一遍。
4. 从升级到加固的完整落地清单
4.1 先确认版本,再决定升级路径
不管你现在是想修复 CVE-2013-7239,还是想顺手排查其他风险,第一步永远是确认版本。很多环境里的 Memcached 是通过系统包管理器安装的,版本长期停留在几百年前的某个小版本。我不知道你的环境怎么样,但我自己见过最多的,就是“能跑就不动”的运维习惯,导致这种漏洞能存活十年以上。
确认版本非常简单,直接在服务器上执行:
memcached -V或者:
memcached -h | head -n 1如果服务已经在运行,还可以看进程启动参数:
ps aux | grep memcached及查看系统包信息。在基于 Debian 或 Ubuntu 的系统上:
dpkg -l | grep memcachedRHEL/CentOS 系统上:
rpm -qa | grep memcached确认版本后,只要版本号低于修复版本(大体上是 1.4.16 之后完成修复,保守起见建议直接升到最近稳定版),就应当立即计划升级。这里我要多说一句:不要只更新二进制文件,还要检查原有启动脚本是不是还沿用旧的配置。升级后我曾经遇到服务起不来,最后发现是旧脚本里带了一个已经不兼容的参数,这也是一种常见情况。
升级步骤可以简化为:
- 在维护窗口前备份 Memcached 启动参数和相关配置。
- 停止服务:
systemctl stop memcached或对应的初始化脚本命令。 - 通过包管理器升级到已修复版本。
- 重新核对启动参数,特别是
-p、-l、-U、-m、-u以及是否需要-S。 - 启动服务并检查监听状态:
ss -lnpt | grep memcached。 - 用一个简单的客户端命令做连通性测试,比如
printf "version\r\n" | nc 127.0.0.1 11211。
升级之后,再用版本检查命令确认一次,避免启动的还是旧二进制。
4.2 补上 SASL 认证校验的“正确姿势”
如果你确实需要开启认证,那么升级后要确保 SASL 环境是完整可用的。Memcached 的 SASL 支持依赖系统里有对应的 SASL 库,并且二进制编译时打开了相关选项。光在启动命令里加-S但库缺失,服务根本起不来。
启用 SASL 的常规步骤大致如下:
- 安装依赖库。常见的是
libsasl2相关包,还有sasl2-bin等工具。 - 配置 SASL 用户。通常会用到
saslpasswd2命令,创建一个用于 Memcached 认证的用户,例如:
saslpasswd2 -a memcached -c cacheuser这个命令会在 memcached 的 SASL 数据库中写入用户cacheuser的密码。具体使用的数据库名称,取决于你的 SASL 配置文件和编译参数,不同发行版存在差异。
- 修改 SASL 配置文件,指定服务对应的机制和数据库路径。典型的配置会放在
/etc/sasl2/memcached.conf。 - 在 Memcached 启动参数中加入
-S。如果你使用 systemd 管理服务,对应参数直接写在 service 文件里。 - 重启服务后,用支持 SASL 的客户端或工具测试,确认未提供正确凭据时无法读写。
这里有一点必须提醒:CVE-2013-7239 是代码缺陷,单靠配置无法修复。如果你因为某种原因暂时无法升级,那不能被“我开了-S”这种想法安慰。必须先做网络层面的隔离,把端口暴露面压到最低,再尽快安排升级。所以“正确姿势”本质上必须包含两个动作:网络隔离兜底 + 版本升级治本。
4.3 用防火墙和监听设置把风险端口隔离
修复一个漏洞,最好的结果不是“漏洞还在但不容易被打”,而是“根本没人能触达这个端口”。就算 Memcached 本身存在未知漏洞,只要外部攻击者无法访问端口,风险也能被控制在一个小范围内。
生产环境中最推荐的监听方式是只绑定业务实际部署的地址。如果应用和 Memcached 在同一台机器上,直接绑定回环地址即可:
memcached -l 127.0.0.1如果业务分布在多台应用服务器上,那就绑定到一个独立的内网管理地址或专用网段地址:
memcached -l 10.10.20.15这样比起-l 0.0.0.0或者干脆不写监听参数,暴露面会小非常多。在很多默认安装里,Memcached 会监听所有可用地址,这是非常危险的。
再叠加防火墙规则。比如在 Linux 上使用 nftables 或 iptables,只允许来自应用服务器对应网段的访问。我可一个简单示例,仅开放内网指定来源访问 TCP 11211:
iptables -A INPUT -p tcp --dport 11211 -s 10.10.20.0/24 -j ACCEPT iptables -A INPUT -p tcp --dport 11211 -j DROP如果你用的是安全的云平台,多数情况下优先配置安全组规则,而不是在虚拟机内部再搞一套 iptables。但无论哪种方式,原则都是同一个:默认拒绝,白名单放行。这里再强调一声,如果你不需要跨机访问缓存,就不要在防火墙上开放这个端口;如果你需要跨机访问,也要把来源限制到最小业务网段,而不是一个大而化之的/8或/0。
4.4 禁用 UDP 端口的注意事项
CVE-2013-7239 本身走的是 TCP 协议,但这并不代表 UDP 端口的风险可以忽略。Memcached 的老版本默认会同时监听 UDP 11211,主要目的是给一些专用客户端提供批量操作通道。但 UDP 没有连接状态,非常容易被用来做源 IP 伪造和流量放大。
所以我在修复 CVE-2013-7239 的同时,几乎无一例外会顺手把 UDP 关掉。只要在启动参数里加一行:
-U 0这个参数的意思就是关闭 UDP 端口,只保留 TCP。如果业务确实依赖 UDP,那至少需要通过防火墙限制来源,并且评估依赖风险。我在实际项目里几乎没遇到过必须依赖 Memcached UDP 的业务,大多数场景直接关掉毫无影响。
升级和加固之后,你最好再检查一次监听端口:
ss -lnutp | grep 11211正常情况应只看到一个 TCP 监听,例如:
LISTEN 0 1024 10.10.20.15:11211 0.0.0.0:* users:(("memcached",pid=1234,fd=11))如果没有 UDP 监听,说明-U 0生效了。
4.5 服务托管层侧的强化
除了 Memcached 本身的启动参数之外,我还建议从系统服务托管层做一些加固。特别是如果你用 systemd 管理,可以给 Memcached 增加一些限制,避免万一进程被攻击者控制后扩大影响面。
这里给一个参考的 service 片段:
[Service] User=memcached Group=memcached ExecStart=/usr/bin/memcached -p 11211 -U 0 -u memcached -m 256 -l 127.0.0.1 -S Restart=on-failure NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true其中NoNewPrivileges=true可以阻止进程获取新的权限提升,ProtectSystem=strict让大部分系统目录变为只读,PrivateTmp=true给进程独立的临时目录。这些措施不一定每条都适合你的场景,但思路是对的:万一缓存服务被攻破,攻击者不会轻易把这一步变成内网横向的大跳板。
如果你没有使用 systemd,而是通过 Supervisor 或脚本方式启动,也可以参考相似的降权思路,确保用独立的低权限用户跑服务,而不是 root。这一点很多人会忽略,但非常关键。缓存服务虽然可以监听在低端口,但完全没有必要用 root 身份运行。
5. 如何自查是否被“光顾”过
5.1 日志层面的检查思路
排查已经发生的事情,比安装一个补丁要难得多。因为 Memcached 本身不是那种日志满天飞的服务,默认情况下它甚至不会记录每一次读写。但这不代表完全无迹可寻。
首先看系统日志。如果你使用 systemd 管理,可以执行:
journalctl -u memcached --since "30 days ago"重点关注有没有认证失败、异常连接、重启记录。开启 SASL 后,Memcached 一般会把认证相关错误记录到日志。比如连接后认证不通过、认证数据处理失败等情况,可能有对应输出。如果日志级别太低,你可能看不到太多东西,但至少可以判断服务是否存在频繁重启或异常崩溃。
如果你是编译安装或使用自定义脚本,那就去/var/log/下找对应的日志文件。完全没有日志的情况下,可以借助系统层面的审计工具。比如auditd可以监控文件访问和系统调用,如果你已经预料到缓存环境存在风险,提前开启审计会比事后瞎猜强得多。
5.2 流量与命令层面的排查
端口暴露过,就一定会留下网络连接记录,只是连接记录本身的保留周期很短。如果事件刚发生不久,你可以先用ss或netstat看当前连接:
ss -tnp | grep 11211观察来源 IP 是否都在预期范围内。如果出现大量非业务来源 IP,或者来源地址分散且规律异常,就要小心了。
需要更完整的证据链时,应该在受控网络环境下抓包。一个基本的抓包命令:
tcpdump -ni eth0 port 11211 -w memcached_traffic.pcap抓一段时间后停止,用 Wireshark 或 tcpdump 回放分析。重点看两个东西:一是数据包是否来自预期网段,二是协议内容里有没有大量异常命令。如果你对 Memcached 二进制协议不太熟,可以先把stats、get、set、flush_all这些明文命令过滤出来看。
还有一种更直接的判断方法,是查看 Memcached 当前运行状态。执行:
printf "stats\r\n" | nc 127.0.0.1 11211输出里会有很多计数器,比如total_connections、cmd_get、cmd_set、get_hits、get_misses。如果total_connections异常高,而业务量并没有增长,很可能有其他来源在不停连接。如果出现连续的flush_all相关迹象,比如命中率剧烈波动,也值得警惕。
5.3 一轮快速的自测验证(授权范围内)
最后,我建议在修补完成后,做一轮“授权范围内的自测”。注意,所有测试只应该在你自己拥有控制权的环境里执行,千万不要针对任何未经授权的系统去验证“能不能绕过认证”。
更稳妥的自测方式是版本信息确认和正常认证流程验证。比如确认版本已经高于修复版本:
printf "version\r\n" | nc 127.0.0.1 11211如果返回的版本足够新,说明 CVE-2013-7239 对应的代码缺陷大概率已经被修复。如果还返回旧版本,那你需要再次检查为什么启动的还是旧二进制。
对于开启 SASL 的环境,可以再用客户端工具验证错误密码是否会被拒绝。具体工具取决于你使用的客户端语言或命令行工具。比如很多 Memcached 客户端都支持--username和--password参数,你可以用一个错误密码测试,确认服务端返回认证失败,而不是静默接受。通过自测,既验证了修复有效性,也能顺便确认你部署的认证逻辑是真的在拦截。
6. 高频问题与实战沉淀
6.1 故障排查速查表
我把这些年处理 Memcached 安全问题时遇到的典型情况和解决方案整理成一张速查表,方便你排查时直接对照。
| 现象 | 可能原因 | 处理建议 |
|---|---|---|
memcached -V显示版本较旧 | 二进制没有升级成功 | 确认包管理器仓库版本,重启后再次检查 |
开启-S后服务启动失败 | SASL 库缺失或配置文件错误 | 检查依赖包、/etc/sasl2/配置文件,确认用户库存在 |
| 客户端普通连接被拒绝认证 | 认证配置确实生效 | 检查客户端是否传入正确的用户名密码 |
| 业务内网可连接,公网也出现大量连接 | 监听地址设为 0.0.0.0 | 改为内网地址并用防火墙限制来源 |
| 升级后原有客户端全部连不上 | 客户端协议版本或 SASL 机制不兼容 | 检查客户端日志,确认是否支持新版本认证机制 |
| 端口既有 TCP 又有 UDP | 未配置-U 0 | 在启动参数中加入-U 0并重启 |
flush_all后命中率骤降但原因不明 | 可能是攻击者清空缓存 | 查看连接来源、CPU 与网络流量,加入监控告警 |
这张表只是一个起点,真实情况往往更复杂。比如你可能会遇到多实例部署、容器化环境、服务发现要求动态端口等情况,需要灵活调整。但排障思路始终如一:先确认版本和配置,再查网络暴露面,然后看审计日志和监控数据。
6.2 我最想告诉后来人的几点经验
这么多年下来,我在 Memcached 相关安全事件上有一个很深的体会:真正出问题的,往往不是漏洞本身,而是团队对“缓存服务”四个字的轻视。这里我多说几句个人经验,希望能帮你少走一些弯路。
第一个经验是:不要用“内网安全”来麻痹自己。现代攻击的起点往往就是某个边缘业务机器被控制,然后通过内网横向移动找到下一个目标。你今天以为内网是干净的,明天可能就有人拿着一台跳板机在你的内网里横向扫描。把 Memcached 绑到内网地址不代表安全,它只代表风险范围没有立刻暴露到公网而已。
第二个经验是:缓存数据的价值需要被认真盘点。我经常问业务团队一个问题:“如果攻击者能读写你的缓存,他能读到什么?能改成什么?”很多人回答不上来。不是因为他们故意隐瞒,而是因为系统演进多年,缓存里的键值早就没人完全清楚了。但一旦你开始做盘点,就会发现里面可能有 session、权限标记、业务配置,甚至一些不该出现的数据。盘点完,你才会真正理解为什么要赶紧修这个漏洞。
第三个经验是:安全修复最好和版本管理绑定在一起。当年 CVE-2013-7239 公告出来的时候,很多运维团队第一时间修补了线上版本。但过了一段时间,新环境搭建时又从旧模板复制了老配置和旧二进制,漏洞又回来了。类似的情况反复发生,所以我很推荐把 Memcached 的版本检查、启动参数检查写进自动化巡检脚本中,而不是靠人脑记忆。
第四个经验是关于异常检测的。Memcached 的默认状态指标太少了,如果你只有进程存活监控而没有业务级监控,很多安全问题会被埋没。哪怕只是最简单的total_connections、cmd_get和get_hits曲线,也能在攻击尚未造成严重后果时提供线索。我的个人习惯是把这些指标接入到已有的监控系统里,设置一个相对保守的告警阈值。不要等到缓存被人 flush 了再去找日志,那基本什么都找不到了。
回头再看 CVE-2013-7239,它确实只是 Memcached 漫长安全史里的一个点。但理解它的成因,能让你顺带建立一套完整的缓存服务加固思路:版本要及时修复、认证要有有效机制、网络要默认拒绝、监控要覆盖业务指标。这套思路不止对这一次漏洞有用,对之后遇到的任何缓存安全问题,同样适用。