第九章:安全配置
深入了解 Redis 安全配置,包括认证、ACL、加密连接和网络安全。
最后更新: 2024-01-15
页面目录
Redis 安全配置
安全是生产环境部署的重要考虑因素。本章介绍 Redis 的各种安全配置选项。
安全架构
┌─────────────────────────────────────────────────────────────────┐
│ Redis 安全架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 网络层安全 │ │
│ │ • 绑定地址 (bind) │ │
│ │ • 防火墙 (iptables/firewalld) │ │
│ │ • TLS/SSL 加密连接 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 认证层 │ │
│ │ • 密码认证 (requirepass) │ │
│ │ • ACL 用户权限控制 │ │
│ │ • 命令重命名/禁用 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 数据层安全 │ │
│ │ • 数据加密(AES) │ │
│ │ • 数据脱敏 │ │
│ │ • 安全审计 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
密码认证
配置密码
# redis.conf
requirepass your_strong_password
# 密码强度建议
# - 至少 32 字符
# - 包含大小写、数字、特殊字符
# - 不使用常见单词
命令行设置
# 启动时设置
redis-server --requirepass password
# 运行时设置
redis-cli CONFIG SET requirepass password
# 查看密码(实际为占位符)
redis-cli CONFIG GET requirepass
连接认证
# 方式一:命令行参数
redis-cli -a password
# 方式二:连接后认证
redis-cli
AUTH password
# 方式三:PING 测试
redis-cli -a password PING
Python 连接
import redis
# 密码认证
client = redis.Redis(
host='localhost',
port=6379,
password='your_password',
decode_responses=True
)
# 测试连接
client.ping()
ACL 访问控制
Redis 6.0 引入了 ACL(Access Control List)功能,支持细粒度的用户权限控制。
ACL 工作原理
┌─────────────────────────────────────────────────────────────────┐
│ ACL 权限模型 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ACL 用户 │ │
│ │ │ │
│ │ user alice on >p1... ~cached:* &@all │ │
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ └── 权限类别 │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ └── 允许的键模式 │ │
│ │ │ │ │ │ │
│ │ │ │ └── 允许的 DSL 编码 │ │
│ │ │ │ │ │
│ │ │ └── 密码(SHA-256 哈希) │ │
│ │ │ │ │
│ │ └── 启用/禁用 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
ACL 命令
# 创建用户
ACL SETUSER alice on '>password' ~cached:* +get +set +del -@all
# 创建只读用户
ACL SETUSER readonly on '>password' ~* -@all +get
# 创建管理员用户
ACL SETUSER admin on '>password' ~* +@all
# 查看用户
ACL LIST
# 查看用户详情
ACL GETUSER alice
# 启用/禁用用户
ACL SETUSER alice off
ACL SETUSER alice on
# 删除用户
ACL DELUSER alice
ACL 权限
| 权限 | 说明 |
|---|---|
| +command | 允许执行命令 |
| -command | 禁止执行命令 |
| +@category | 允许某类命令 |
| -@category | 禁止某类命令 |
| ~patterns | 允许的键模式 |
| #password | 设置密码 |
| >password | 清除密码 |
命令类别
| 类别 | 包含命令 |
|---|---|
| @read | GET, HGET, LRANGE 等 |
| @write | SET, DEL, LPUSH 等 |
| @admin | ADMIN, CONFIG, SHUTDOWN 等 |
| @dangerous | FLUSHDB, KEYS, SCRIPT 等 |
| @all | 所有命令 |
| @none | 无命令权限 |
ACL 配置示例
# redis.conf
user alice on >argon2_hash ~cached:* +get +set +del +exists ~session:* +get
user readonly on >hash123 ~* -@admin +@read -@dangerous
user admin on >admin456 ~* +@all
user app on >'SHA256-CryptedPassword' ~app:* +get +set +hget +hset +@read +@write
Python ACL 连接
import redis
# 使用 ACL 用户连接
client = redis.Redis(
host='localhost',
port=6379,
username='alice',
password='password',
decode_responses=True
)
client.set('cached:user:1', 'value')
命令重命名与禁用
重命名命令
# 命令重命名(增加安全性)
rename-command CONFIG b840fc02d524045429341cc3e480787fac704bfa
rename-command FLUSHDB b840fc02d524045429341cc3e480787fac704bfb
rename-command FLUSHALL b840fc02d524045429341cc3e480787fac704bfc
rename-command KEYS b840fc02d524045429341cc3e480787fac704bfd
rename-command SHUTDOWN ""
安全建议
| 命令 | 建议操作 |
|---|---|
| FLUSHDB | 重命名为复杂字符串 |
| FLUSHALL | 禁用或重命名 |
| KEYS | 重命名为复杂字符串 |
| SHUTDOWN | 重命名 |
| CONFIG | 限制访问 |
| EVAL/EVALSHA | 限制执行 |
网络安全
绑定地址
# 只绑定本地
bind 127.0.0.1
# 绑定内网 IP
bind 127.0.0.1 192.168.1.100
# 保护模式(无绑定且无密码时拒绝外部连接)
protected-mode yes
防火墙配置
# iptables (Linux)
# 允许特定 IP 访问 Redis
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 6379 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROP
# firewalld (CentOS)
firewall-cmd --permanent --add-port=6379/tcp
firewall-cmd --permanent --add-source=192.168.1.0/24
firewall-cmd --reload
TLS/SSL 加密
# TLS 配置
tls-port 6380
port 0 # 禁用非 TLS 连接
tls-cert-file /etc/redis/tls/redis.crt
tls-key-file /etc/redis/tls/redis.key
tls-ca-cert-file /etc/redis/tls/ca.crt
# TLS 认证模式
tls-auth-clients no # 可选:客户端证书认证
tls-auth-clients optional # 可选:客户端证书认证
生成证书
# 生成私钥
openssl genrsa -out redis.key 2048
# 生成证书请求
openssl req -new -key redis.key -out redis.csr
# 自签名证书
openssl x509 -req -in redis.csr -signkey redis.key -out redis.crt
# CA 证书(如果有)
openssl x509 -req -in redis.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out redis.crt
Docker TLS 配置
services:
redis:
image: redis:7-alpine
ports:
- "6380:6380"
volumes:
- ./tls:/etc/redis/tls
command: |
redis-server --port 6380
--tls-port 6380
--tls-cert-file /etc/redis/tls/redis.crt
--tls-key-file /etc/redis/tls/redis.key
--tls-ca-cert-file /etc/redis/tls/ca.crt
--tls-auth-clients optional
数据安全
数据加密(应用层)
from cryptography.fernet import Fernet
import redis
# 生成密钥(安全存储)
key = Fernet.generate_key()
cipher = Fernet(key)
# 加密数据
def encrypt_data(data):
return cipher.encrypt(data.encode())
def decrypt_data(encrypted_data):
return cipher.decrypt(encrypted_data).decode()
# Redis 操作
client = redis.Redis(host='localhost', port=6379, password='pwd')
# 存储加密数据
encrypted = encrypt_data("sensitive data")
client.set('key', encrypted)
# 读取解密数据
encrypted = client.get('key')
decrypted = decrypt_data(encrypted)
数据脱敏
def mask_phone(phone):
"""手机号脱敏:138****1234"""
if len(phone) == 11:
return phone[:3] + '****' + phone[-4:]
return phone
def mask_id_card(id_card):
"""身份证脱敏:320***********1234"""
if len(id_card) == 18:
return id_card[:3] + '*' * 11 + id_card[-4:]
return id_card
# 存储时脱敏
masked_phone = mask_phone("13812341234")
client.hset('user:1', 'phone', masked_phone)
安全审计
开启命令日志
# 命令日志
slowlog-log-slower-than 0 # 记录所有命令
slowlog-max-len 10000
# 或使用 Redis 审计插件
# RedisGears, RedisGrove 等
Lua 脚本安全
# 限制 Lua 脚本执行
# 配置最大内存和执行时间
lua-time-limit 5000
# 沙箱限制
# Lua 脚本只能访问有限的功能
监控脚本
#!/usr/bin/env python3
import redis
import json
import logging
from datetime import datetime
class RedisSecurityMonitor:
def __init__(self):
self.client = redis.Redis(host='localhost', port=6379,
password='pwd', decode_responses=True)
self.logger = logging.getLogger('security')
def check_failed_auth(self):
"""检查认证失败"""
info = self.client.info('clients')
# 检查 clients_clipped 或其他指标
pass
def scan_dangerous_keys(self):
"""扫描危险键"""
dangerous = []
for key in self.client.scan_iter('*'):
# 检查异常键名
if any(x in key.lower() for x in ['admin', 'password', 'secret']):
dangerous.append(key)
return dangerous
def audit_access(self):
"""审计访问"""
# 记录访问日志
pass
if __name__ == '__main__':
monitor = RedisSecurityMonitor()
dangerous = monitor.scan_dangerous_keys()
if dangerous:
print(f"发现敏感键: {dangerous}")
生产环境安全配置
# 生产环境推荐配置
# /etc/redis/production.conf
# 网络安全
bind 127.0.0.1 <internal_ip>
protected-mode yes
port 6379
tcp-backlog 511
timeout 300
tcp-keepalive 300
# 密码认证
requirepass <strong_password>
# ACL
user default on nopass ~* &* +@all
# 命令限制
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command KEYS "8b7c92a1f0d3e4f5"
rename-command SHUTDOWN "7h9k0m1n2b3v4c5"
# 日志
loglevel notice
logfile /var/log/redis/redis.log
# 内存
maxmemory 4gb
maxmemory-policy allkeys-lru
# 持久化
save 900 1
save 300 10
save 60 10000
# AOF
appendonly yes
appendfsync everysec
安全检查清单
┌─────────────────────────────────────────────────────────────────┐
│ Redis 安全检查清单 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 网络安全: │
│ □ bind 只绑定必要地址 │
│ □ protected-mode 启用 │
│ □ 防火墙限制访问 │
│ □ 生产环境启用 TLS │
│ │
│ 认证授权: │
│ □ 设置强密码 │
│ □ 使用 ACL 细粒度控制 │
│ □ 禁用或重命名危险命令 │
│ □ 定期轮换密码 │
│ │
│ 数据安全: │
│ □ 敏感数据加密存储 │
│ □ 数据脱敏处理 │
│ □ 定期备份 │
│ │
│ 监控审计: │
│ □ 开启慢查询日志 │
│ □ 监控异常访问 │
│ □ 记录审计日志 │
│ │
└─────────────────────────────────────────────────────────────────┘
下一步
接下来让我们学习 Redis 性能优化。
👉 性能优化