☰
memcached未授权访问修复实战:从端口暴露到安全收口
2026/10/20 6:45:10 网站建设 项目流程

接手一台老机器做例行安全巡检时,我扫到11211端口直接暴露在外网。随手用测试机连上去,敲了一行stats,缓存里的会话标识、临时数据立刻列了出来——典型的 memcached 未授权访问漏洞。这类问题在业务系统里出现频率极高,因为 memcached 默认不带鉴权机制,从网络层到数据层都处于“裸奔”状态。这篇内容会完整记录我从发现、分析、修复到验证的全过程,包括边界收口思路、具体配置、验证手法和几个容易踩的坑,希望能帮你少走弯路。

1. 漏洞的本质与被利用的真实场景

1.1 未授权访问这四个字,具体是什么意思

memcached 是一个高性能的分布式内存缓存系统,基于 KV 结构,默认监听 11211 端口。绝大多数团队把它放在数据库前面,用来缓存热点查询结果、用户会话、临时计数等数据。它的设计初衷是部署在可信内网,所以协议本身极简,没有任何账号密码的概念。服务启动后,只要有客户端能连到 11211 端口,就可以直接执行数据操作命令,完全不需要身份验证。

这句“完全不需要身份验证”意味着什么呢?攻击者只要 TCP 能连上,就能做下面这些事:

  • 用stats命令查看版本、运行时间、当前缓存数量等元信息,快速确认服务类型。
  • 用get遍历已知 key,读取缓存的敏感数据。如果应用把用户 token、下单信息、接口响应放在里面,这些数据就等于公开了。
  • 用set往缓存里写入自定义数据,实现缓存投毒。业务代码如果无脑信任缓存结果,就可能被污染的数据带偏。
  • 用flush_all一键清空所有缓存,让全部热点请求瞬间穿透到数据库,直接把后端打挂。
  • 通过海量写入把内存占满,让合法数据频繁淘汰,拖垮正常业务。

这些风险里,缓存清空和缓存投毒造成的破坏往往是即时的,但更隐蔽的是数据泄露。很多团队对缓存里的数据不敏感,总觉得“反正是临时数据”,可实际生产环境里,会话ID、验证码、用户资料临时副本都可能在缓存中停留一段时间,一旦暴露,就相当于把身份凭证递到别人手里。

1.2 这种漏洞是怎么被发现的

在公网环境下,攻击者发现这种漏洞的成本极低。流程通常是先做端口扫描,发现 11211 端口开放,然后尝试连接并执行一条stats。只要返回了STAT version之类的输出,就确认这是一个存在未授权访问的 memcached 节点。

值得注意的是,memcached 的 UDP 协议历史上还曾被用作流量反射放大的工具:攻击者伪造源地址,向开启了 UDP 11211 的服务端发送小请求,服务端把大量数据回给被伪造的受害者地址。2018 年前后曝出过大规模利用事件,影响面非常广。从那时起,业内普遍形成共识:memcached 暴露在公网本身就是一件高风险的事,即使没有数据泄露,也可能被卷进 DDoS 攻击链里。

另外,这类问题经常充当内网横向移动的跳板。有些公司外网防护做得不错,但某台研发测试机或者某台堡垒机把 11211 端口开到了内网段。攻击者进入内网后,扫描一遍 11211、6379、27017 这类常见端口,很容易再找到几个不设防的服务,扩大战果。所以你在公网发现一台,不代表问题只存在于这一台,顺着网段继续排查往往是必要的。

2. 修复思路:为什么“藏起来”比“上锁”更优先

2.1 四种方案横向对比

先说结论:修复 memcached 未授权访问,不是只有“启用认证”一条路,甚至很多时候认证并不是首选。下面是四种常用思路的对比。

方案操作复杂度见效速度副作用适用场景
安全组/防火墙限制来源 IP低快可能影响合法来源所有场景,尤其是云上
修改监听地址 bind 到内网低快本机进程需配合单机或少数业务机访问
启用 SASL 认证高中客户端要改代码网络隔离做不到时的兜底
停用/卸载服务最低最快业务不可用已经不再使用的服务

我见过很多人一听说“未授权访问”,第一反应就是去翻 memcached 的认证配置。这其实是把思路局限住了。memcached 的定位是内网可信组件,它的认证机制不像数据库那样成熟,客户端对 SASL 的支持也参差不齐。要真走认证路线,往往要动到应用代码,远不如网络层收口来得干净。

2.2 从 memcached 的定位看访问控制

如果把运维比作看门,那 memcached 这种服务更适合“把门藏起来”,而不是“给门加十把锁”。为什么?

首先,memcached 的协议非常简单,客户端库五花八门。启用 SASL 认证后,每个接入方都得支持认证握手流程,一些老旧的客户端甚至根本不支持带认证的连接。为了修一个漏洞,让所有业务方排期改代码,成本高、周期长,而且不一定能推动落地。

其次,网络层的访问控制对所有服务一视同仁。你可以在云安全组里只放行业务服务器的内网 IP,也可以在本机防火墙里做同样的限制。这个规则不仅保护 memcached,将来如果同一台机器上还有其他内网服务,也能顺带罩住。更重要的是,网络层规则不受应用代码影响,改完即时生效,也不存在兼容性问题。

最后,访问控制是最小权限原则的直接体现。memcached 本来就不需要暴露给公网,也不需要暴露给无关内网主机。既然不需要,那就直接从路由层面把它取消,比在服务层加一万道验证都来得实在。

2.3 什么情况下才需要走认证路线

有一种场景确实需要考虑 SASL 认证:业务方由于某种原因,必须把 memcached 放到一个无法做网络隔离的网络上,比如混合云链路没打通、某些老架构里的特殊网关段等。这种情况下,网络层没法收口,认证就成为最后一道防线。

但在启用之前,你要有五点心理准备。第一,memcached 需要编译时开启 SASL 支持,很多发行版自带的包未必有;第二,需要额外维护一套账号密码,凭据管理不当又会引入新风险;第三,应用代码和客户端库都得支持认证,改造量不小;第四,memcached 的 SASL 只对数据操作做校验,外部探测扫描依然存在;第五,一旦有人拿到服务器权限,配置文件里的凭据直接暴露。所以认证方案的技术可行,但在实际运营里,它更多是“无奈之举”,不应该是第一选择。

3. 实操修复的三种主流方案

3.1 第一步:先确认服务是怎么部署的

修复之前,先搞清一件事:memcached 正在以什么形态运行。常见的部署方式有三种:系统包管理器安装的守护进程、源码编译的手动进程、Docker 容器。不同形态对应的修改方式完全不同。

先用这三条命令看状态:

ss -lntp | grep 11211 systemctl status memcached 2>/dev/null ps -ef | grep memcached | grep -v grep

ss -lntp能告诉你监听地址是不是 0.0.0.0,也能显示出进程 PID。systemctl status能看出服务是不是 systemd 管理。ps -ef能直接看到启动参数里有没有-l、-U这类关键配置。如果进程是通过 Docker 起的,ps -ef里会出现容器进程的痕迹,再配合docker ps就能看清端口映射关系。

这一步的目的是确定你要改的文件和参数在哪个层面,避免后面改了半天,服务根本没加载你改的配置。

3.2 方案一:把监听地址从 0.0.0.0 收拢到内网

这是最直接有效的修复动作。memcached 默认监听所有网卡,也就是说绑定在 0.0.0.0 上。我们可以在启动配置里加上-l参数,指定它只监听 127.0.0.1 或者业务内网地址。

在 Debian 系的常见配置路径/etc/memcached.conf里,改法是直接增加或修改:

-p 11211 -l 192.168.10.10 -U 0 -m 1024 -c 1024

如果是基于 RPM 的发行版,常见配置路径是/etc/sysconfig/memcached,改法是在OPTIONS变量中追加:

OPTIONS="-l 192.168.10.10 -U 0"

修改后重启服务:

systemctl restart memcached

重启完立刻检查监听地址:

ss -lntp | grep 11211

正常会看到192.168.10.10:11211,而不是0.0.0.0:11211。

关于-l的值,这里有一条清晰的分界线:如果只有本机的应用需要访问缓存,绑定127.0.0.1就够了。如果其他服务器也需要连这台 memcached,就要绑定一个实际生效的内网 IP,比如192.168.10.10。绑定内网 IP 后,公网依然连不进来,但内网业务机可以正常访问,这也是生产环境最常见的做法。

另外,配置里的-U 0建议显式加上,它的作用是关闭 UDP 监听。memcached 对 UDP 的默认策略在不同版本和发行版里不一致,与其依赖默认值,不如直接关掉来得明确。

3.3 方案二:云安全组与本机防火墙双重收口

如果服务绑定地址调整受限于网络拓扑,或者你希望多一层防御,下一步就是做访问控制。

本机防火墙的 iptables 规则可以这样配:

# 放行业务服务器来源 iptables -A INPUT -s 192.168.10.10 -p tcp --dport 11211 -j ACCEPT # 拒绝其余所有来源 iptables -A INPUT -p tcp --dport 11211 -j DROP

注意规则顺序:放行规则必须写在拒绝规则之前。iptables 是自上而下匹配的,先命中了 ACCEPT 才会放行,如果 DROP 写在前,后面所有来源都会被拦掉。

如果你用的是 firewalld,思路一样,只是命令语法变成 rich rule:

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.10" port protocol="tcp" port="11211" accept' firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port protocol="tcp" port="11211" reject' firewall-cmd --reload

云安全组层面,你需要在入方向规则里加一条:协议 TCP,端口 11211,来源地址只填写业务服务器的内网 IP,保存后立即生效。这里一定要刻意去检查一条规则:来源地址是不是误填了0.0.0.0/0。这个低级错误我见过太多次了,安全组本身是好的,一招写错全盘皆输。

有些朋友觉得,既然云安全组已经限制了来源 IP,本机防火墙还要不要再配?我的建议是配。安全组和本机防火墙是两层独立的控制面,任何一层出现配置漂移或误操作,另一层还能兜底。这类服务暴露面越小越好,多做一层并不亏。

3.4 方案三:SASL 认证作为兜底

如果你确实被逼到必须走认证路线,memcached 的 SASL 支持是唯一可用的官方机制。先说检查方式:

memcached -h | grep -i sasl

输出里如果能看到 SASL 相关字眼,说明当前二进制支持;否则需要重编或换包。启用步骤大致是:安装cyrus-sasl相关依赖,配置/etc/sasl2/memcached.conf,创建sasldb用户,最后在启动参数里加上-S。

配置示例:

sasldb_path: /etc/sasl2/ssasldb

创建用户:

saslpasswd2 -a memcached -c 用户名 -f /etc/sasl2/ssasldb

重启 memcached 后再用客户端连接,就需要携带用户名密码了。

这个方案我实际推进过,印象最深的是客户端适配问题。业务方使用的语言 SDK 版本不一,有的库从设计上就没把认证集成进来。改完服务端,还得推动所有调用方升级、改配置、联调,整个周期拖得很长。所以在前面我也再三强调,网络层能收口就绝不走认证,这不是懒,是从落地效率出发的理性选择。

3.5 Docker 部署的特殊修复姿势

容器化部署是另一种高发场景,而且有个特别容易踩的坑。

如果你用docker run -p 11211:11211的方式启动 memcached,宿主机上看到的就是 0.0.0.0:11211,因为 Docker 的端口映射会把宿主机所有网卡的端口都转发给容器。这时候哪怕你进到容器里把 memcached 的-l改成 127.0.0.1,外部访问依然能通过宿主机的转发进去,容器层面的监听地址根本起不到作用。

修复思路有三种:

  • 在宿主机上做防火墙/安全组限制,只允许业务机来源。
  • 调整 Docker 端口绑定,用-p 127.0.0.1:11211:11211只让本机访问,或者干脆取消端口映射,改用--network host让容器直接共享宿主机网络栈。
  • 如果容器与业务机都在同一 overlay 网络里,可以去掉对宿主机的端口暴露,只在 Docker 网络内互通。

如果是docker-compose部署,同样要检查 ports 段里的映射关系:

ports: - "192.168.10.10:11211:11211"

把宿主机监听地址限定到内网 IP,也算一种收敛方式。

4. 验证修复效果:既证明外部进不来,也证明业务没断

4.1 外部探测视角

修复完成后,不要只看配置或者看监听端口,一定要站在攻击者的视角再测一遍。最简单的方式,在外部测试机执行:

nc -zv 目标IP 11211

修复后的预期结果是连接超时或拒绝。也可以直接发一条协议命令试试:

printf 'stats\r\nquit\r\n' | nc -w3 目标IP 11211

如果没有任何输出,说明外部已经无法读到服务信息,修复生效。

用 nmap 的 memcached 脚本验证也可以:

nmap -p 11211 --script memcached-info 目标IP

运行后如果脚本显示无法连接,说明目标的服务信息没有暴露给外部扫描者。

这里有一个容易误判的点:如果 bind 改成了内网 IP,公网虽然连不上,但内网机器还是可以访问。这并不代表漏洞没修复,反而说明收口范围是符合预期的。判断的标准应该是“不需要访问的人能不能进来”,而不是“所有人都不许访问”。

4.2 业务连通性视角

修复安全漏洞最容易犯的另一个错,就是把自己的合法业务也拦在门外。所以验证完外部不可达,紧接着要在业务服务器上测一次真实读写。

在业务机上执行:

printf 'set testkey 0 60 3\r\nabc\r\n' | nc 目标内网IP 11211 printf 'get testkey\r\nquit\r\n' | nc 目标内网IP 11211

如果第二个命令返回VALUE testkey 0 3和abc,说明业务侧连接、写入、读取都正常。

如果业务机连不上,先看绑定的 IP 是不是写错了,再看防火墙和安全组的来源 IP 是否包含当前业务机。很多时候不是方案本身有问题,而是规则里漏掉了某台机器。

4.3 把检查固化成一个巡检脚本

一次性的验证代表不了长期安全,我习惯把检查写成脚本,定时跑一遍。比如下面这个简单的 Python 巡检脚本:

import socket import sys def check_memcached(host, port=11211, timeout=3): try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(timeout) s.connect((host, port)) s.send(b"stats\r\n") data = s.recv(1024) if b"STAT version" in data or b"STAT pid" in data: print(f"[!] {host}:{port} 存在未授权访问风险") else: print(f"[?] {host}:{port} 有响应但未识别") except (socket.timeout, ConnectionRefusedError, OSError): print(f"[+] {host}:{port} 无法连接,端口已收敛") if __name__ == "__main__": for host in sys.argv[1:]: check_memcached(host)

用法:

python3 memcache_check.py 192.168.10.10 192.168.10.11 192.168.10.12

把这个脚本加到定时任务里,每周扫一次核心业务网段的 11211 端口,能有效防止某个服务实例被重新拉起时带出旧配置。

5. 踩坑记录与大坑排查实录

5.1 改完配置,监听地址为什么还是 0.0.0.0

这是出现频率最高的一个问题。你明明改了/etc/memcached.conf里的-l,重启后ss -lntp一看还是 0.0.0.0。

原因是 systemd 启动的服务未必读取那个路径的配置文件。很多发行版的 memcached 服务单元文件里,直接在ExecStart行写死了启动参数,配置文件只是参考。你需要用systemctl cat memcached看实际执行的命令,判断参数从哪里来。

如果发现ExecStart里写的是-l 0.0.0.0,那直接改单元文件:

sudo systemctl edit memcached

在 override 段覆盖ExecStart,加上-l 192.168.10.10 -U 0。改完记得systemctl daemon-reload再重启。

5.2 iptables 规则重启就丢,怎么办

iptables 的规则默认只保存在内存里,重启后就会消失。很多人配完规则当时测试是通的,过几天一重启,端口又暴露了。这是运维里特别经典的“假修复”。

解决方式是把规则持久化保存。用过iptables-save导到文件,再用开机脚本加载。如果不想手工搞,就换用 firewalld 或者 ufw 这类带持久化能力的前端,它们的规则本身就是落盘的,省心不少。

5.3 业务服务器连不上,十有八九是这几个原因

修复后业务连不上,优先查这四件事:

  • -l绑定的地址是否写成了 127.0.0.1,导致其他机器无法访问。这种情况改成内网 IP 即可。
  • 云安全组的来源 IP 是否填错。尤其是多网卡、多账号的复杂网络,容易把另一台业务机的 IP 写成网卡子接口的地址。
  • 本机防火墙规则顺序是否错误。DROP 规则写在了 ACCEPT 前面,就会把所有来源都拒掉。
  • 业务机自己出方向的防火墙规则是否限制了到目标端口访问。

排查时用tcpdump -i eth0 port 11211在 memcached 服务器上看有没有包到达,再对照防火墙规则看是否被丢弃,能很快定位到是哪一层的问题。

5.4 别忘了 UDP 端口

有些版本会同时监听 UDP 11211,而ss -lntp只看 TCP 端口,会漏掉这个风险。检查时用:

ss -ulnp | grep 11211

如果看到 UDP 监听,就在启动参数里加上-U 0。这个细节,安全扫描报告通常不会帮你写清楚,但反射放大攻击的主要利用面恰恰是 UDP,必须自己主动关掉。

6. 后续加固与日常监督

6.1 全量排查同类“裸奔”服务

修完一台 memcached,不建议马上收工。反过来想,既然这台机器曾经过于开放,那同网段里其他机器是否也有类似问题?我习惯在收口之后立刻做一轮同类服务排查,重点关注:

  • Memcached 的 11211 端口
  • Redis 的 6379 端口
  • MongoDB 的 27017 端口
  • Elasticsearch 的 9200/9300 端口

这些服务的共同点是早期版本都不强制认证,新版本虽有改进,但默认配置未必安全。批量扫一遍自己管理的网段,把开放面整理成清单,再逐个评估是否需要收口,比被动等漏洞报告高效得多。

这个排查动作本身很简单,发到每台机器的 agent 上跑一条命令:

ss -lntup | grep -E '11211|6379|27017|9200|9300'

收集结果后集中看监听地址是不是 0.0.0.0,是的话就进入整改流程。

6.2 把修复沉淀成配置和监控制度

单机手动修复只是临时状态。memcached 这类服务,如果配置是靠一台一台机器手改的,过几个月新上线的实例很可能又恢复成默认配置。要让修复真正长期有效,需要把配置固化到自动化分发体系里,比如配置管理工具统一推送/etc/memcached.conf,然后通过 service 单元文件统一管理启动参数。新机器一上线就带正确的绑定和 UDP 关闭参数,不再依赖人工判断。

同时我建议做一个很轻量的监控规则:监听端口变化检测。很多监控平台都能对端口做拨测,当发现 0.0.0.0:11211 出现时立刻告警。这类告警不一定代表被攻击,但它能让你在新隐患影响业务之前就收到消息。

6.3 一点个人体会

修复这类型漏洞,我自己的经验是不要只盯着服务本身。memcached 未授权访问的本质,不是“memcached 没有密码”,而是“这台机器不该开放的端口对外开放了”。想清楚这一点,修复思路就会从“给服务加锁”自动转成“收拢暴露面”。从监听地址、防火墙、安全组到业务连通性,这四个环节逐项验证,基本就能确保修复既到位又不影响线上业务。

我后来养成的习惯是,任何需要对外提供服务的新组件上线,第一件事先看它默认绑定在哪个地址上,而不是先写业务代码。把“默认收口”变成肌肉记忆,很多安全风险根本不会走到爆发那一步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询