第 11 章:安全加固
第 11 章:安全加固
11.1 安全威胁概览
Memcached 默认无认证、无加密、无访问控制,这使其成为常见的攻击目标。
主要威胁
| 威胁 | 说明 | 严重程度 |
|---|
| 未授权访问 | 默认无认证,任何人可读写 | ★★★★★ |
| DDoS 放大攻击 | UDP 反射放大 51,000 倍 | ★★★★★ |
| 数据泄露 | 缓存中的敏感数据被窃取 | ★★★★ |
| 缓存投毒 | 恶意写入虚假缓存数据 | ★★★★ |
| 缓存雪崩 | flush_all 导致大量回源 | ★★★ |
UDP 反射放大攻击原理
Memcached UDP 反射放大攻击:
1. 攻击者伪造源 IP 为受害者 IP
2. 向 Memcached UDP 端口发送小型请求(约 15 字节)
3. Memcached 返回大量数据(约 750KB)到受害者 IP
4. 放大倍数: 750KB / 15B ≈ 51,200 倍!
预防措施:
- 禁用 UDP: -U 0
- 不暴露公网
- 防火墙规则
11.2 网络隔离(最重要)
绑定内网地址
# 只监听本机(开发环境)
memcached -l 127.0.0.1
# 只监听内网(生产环境)
memcached -l 10.0.1.10
# 禁用 UDP(防 DDoS 放大)
memcached -U 0
# 绑定 Unix Socket(最安全)
memcached -s /var/run/memcached/memcached.sock -a 0770
防火墙规则
# iptables:只允许应用服务器访问
sudo iptables -A INPUT -p tcp --dport 11211 -s 10.0.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 11211 -j DROP
# 禁止 UDP(防止 DDoS 放大)
sudo iptables -A INPUT -p udp --dport 11211 -j DROP
# firewalld (CentOS)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" port port="11211" protocol="tcp" accept'
sudo firewall-cmd --reload
# nftables (现代 Linux)
sudo nft add rule inet filter input ip saddr 10.0.1.0/24 tcp dport 11211 accept
sudo nft add rule inet filter input tcp dport 11211 drop
sudo nft add rule inet filter input udp dport 11211 drop
网络架构
推荐的网络架构:
Internet
│
▼
┌──────────┐
│ 防火墙 │
└────┬─────┘
│
▼
┌──────────┐ ┌────────────────────┐
│ Web 层 │────▶│ 应用层网络 (10.0.1) │
│ (DMZ) │ │ │
└──────────┘ │ ┌──────────────┐ │
│ │ Memcached 集群│ │
│ │ (内网隔离) │ │
│ └──────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ 数据库 │ │
│ └──────────────┘ │
└────────────────────┘
11.3 SASL 认证
SASL(Simple Authentication and Security Layer)是 Memcached 支持的认证机制。
编译启用 SASL
# 需要从源码编译
sudo apt-get install -y libsasl2-dev
cd /tmp/memcached-1.6.31
./configure --enable-sasl
make -j$(nproc)
sudo make install
启动 SASL
# 创建 SASL 配置文件
cat > /etc/sasl2/memcached.conf <<EOF
mech_list: plain
log_level: 5
sasldb_path: /etc/sasl2/memcached-sasldb2
EOF
# 创建用户
sudo saslpasswd2 -a memcached -c admin -f /etc/sasl2/memcached-sasldb2
# 输入密码
# 启动 Memcached(启用 SASL)
memcached -S -m 128 -p 11211
使用 SASL 认证
# 使用 sasl 手动认证(二进制协议)
# 需要编写客户端程序
import bmemcached
# 使用带 SASL 认证的客户端
mc = bmemcached.Client(
['localhost:11211'],
username='admin',
password='secret_password'
)
mc.set('key', 'value')
print(mc.get('key')) # value
<?php
$mc = new Memcached();
$mc->setSaslAuthData('admin', 'secret_password');
$mc->addServer('localhost', 11211);
$mc->set('key', 'value');
echo $mc->get('key');
SASL 的局限性
| 局限 | 说明 |
|---|
| 性能开销 | 每次连接需要认证握手 |
| 只支持二进制协议 | 文本协议不支持 SASL |
| 无 ACL | 只有认证,没有授权(所有人看到所有数据) |
| 密码管理 | 需要 saslpasswd2 工具管理 |
11.4 TLS 加密
Memcached 1.6.3+ 支持 TLS 传输加密。
编译启用 TLS
sudo apt-get install -y libssl-dev
cd /tmp/memcached-1.6.31
./configure --enable-tls
make -j$(nproc)
sudo make install
配置 TLS
# 生成证书(测试用)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-days 365 -nodes -subj "/CN=memcached"
# 启动 TLS
memcached \
--enable-tls \
--tls-cert cert.pem \
--tls-key key.pem \
--tls-protl 1.2 \
-m 128 \
-p 11212
# 带客户端证书验证(双向 TLS)
memcached \
--enable-tls \
--tls-cert cert.pem \
--tls-key key.pem \
--tls-ca-cert ca.pem \
--tls-verify-mode 3 \
-m 128
| TLS 参数 | 说明 |
|---|
--enable-tls | 启用 TLS |
--tls-cert | 服务端证书 |
--tls-key | 服务端私钥 |
--tls-ca-cert | CA 证书(用于验证客户端) |
--tls-verify-mode | 验证模式(1=服务端验证客户端, 2=客户端验证服务端, 3=双向) |
--tls-protl | 最低 TLS 版本(1.2 推荐) |
--tls-ciphers | 允许的加密套件 |
--shutdown-query | TLS 关闭行为 |
11.5 访问控制策略
应用层代理
使用代理层实现细粒度访问控制:
Client → Proxy (认证 + 路由 + 过滤) → Memcached
推荐代理:
- mc-router
- mcrouter (Facebook)
- twemproxy (Twitter)
- Codis
mc-router 示例
# 安装 mc-router
docker pull grumpycoders/mc-router:latest
# 配置路由规则
cat > routes.json <<EOF
{
"routes": {
"user:*": "mc-user:11211",
"session:*": "mc-session:11211",
"*": "mc-default:11211"
}
}
EOF
# 启动 mc-router
docker run -d --name mc-router \
-p 11211:11211 \
-v $(pwd)/routes.json:/config/routes.json \
grumpycoders/mc-router:latest \
--config /config/routes.json
应用层 Key 前缀隔离
class IsolatedMemcachedClient:
"""带命名空间隔离的客户端"""
def __init__(self, mc, namespace):
self.mc = mc
self.namespace = namespace
def _key(self, key):
return f"{self.namespace}:{key}"
def get(self, key):
return self.mc.get(self._key(key))
def set(self, key, value, ttl=0):
return self.mc.set(self._key(key), value, time=ttl)
def delete(self, key):
return self.mc.delete(self._key(key))
# 使用
user_mc = IsolatedMemcachedClient(mc, "app1:user")
session_mc = IsolatedMemcachedClient(mc, "app1:session")
user_mc.set("1001", data) # 实际 Key: app1:user:1001
session_mc.set("abc", session) # 实际 Key: app1:session:abc
11.6 安全检查清单
部署前安全检查
#!/bin/bash
# Memcached 安全检查脚本
echo "=== Memcached 安全检查 ==="
# 1. 检查监听地址
LISTEN=$(echo "stats settings" | nc localhost 11211 | grep interface)
echo "监听地址: $LISTEN"
if echo "$LISTEN" | grep -q "0.0.0.0"; then
echo "⚠️ 警告: 监听所有接口,建议限制为内网 IP"
fi
# 2. 检查 UDP 端口
UDP_PORT=$(echo "stats settings" | nc localhost 11211 | grep udpport)
echo "UDP 端口: $UDP_PORT"
if [ "$UDP_PORT" != "0" ] && [ -n "$UDP_PORT" ]; then
echo "⚠️ 警告: UDP 端口已开启,可能导致 DDoS 放大攻击"
fi
# 3. 检查最大连接数
MAX_CONN=$(echo "stats settings" | nc localhost 11211 | grep maxconns)
echo "最大连接数: $MAX_CONN"
# 4. 检查 SASL
echo "stats settings" | nc localhost 11211 | grep sasl
echo "SASL 认证: $(echo "stats settings" | nc localhost 11211 | grep sasl)"
# 5. 检查 TLS
echo "stats settings" | nc localhost 11211 | grep tls
# 6. 检查外部连接
echo "当前外部连接:"
ss -tn | grep :11211 | grep -v 127.0.0.1 | wc -l
echo "=== 检查完成 ==="
扩展阅读
小结
| 要点 | 内容 |
|---|
| 第一要务 | 绑定内网地址 -l 10.x.x.x,禁用 UDP -U 0 |
| 防火墙 | 只允许应用服务器 IP 访问 11211 端口 |
| SASL | 启用认证(需二进制协议),适合内网多租户 |
| TLS | 1.6.3+ 支持传输加密,适合跨机房通信 |
| 代理层 | mc-router / mcrouter 实现路由和访问控制 |