Files
Cloud-book/数据库/Redis_2025/10_Redis安全管理.md
2025-08-27 17:10:05 +08:00

18 KiB
Raw Permalink Blame History

Redis 安全管理

访问控制

密码认证

Redis 提供了基本的密码认证机制来保护数据安全。

配置密码认证

# 在配置文件中设置密码
# /etc/redis/redis.conf
requirepass your_strong_password

# 动态设置密码
redis-cli CONFIG SET requirepass "your_strong_password"

# 使用密码连接
redis-cli -a your_strong_password

# 或者连接后认证
redis-cli
127.0.0.1:6379> AUTH your_strong_password
OK

密码安全最佳实践

# 生成强密码
openssl rand -base64 32

# 密码复杂度要求:
# - 长度至少16位
# - 包含大小写字母、数字、特殊字符
# - 避免使用字典词汇
# - 定期更换密码

# 示例强密码
requirepass "Rd!s@2024#Str0ng&P@ssw0rd"

用户管理 (ACL)

Redis 6.0+ 引入了 ACLAccess Control List功能提供更细粒度的权限控制。

ACL 基本概念

# 查看当前用户
ACL WHOAMI

# 列出所有用户
ACL LIST

# 查看用户详细信息
ACL GETUSER username

# 查看当前用户权限
ACL GETUSER default

创建和管理用户

# 创建只读用户
ACL SETUSER readonly on >readonly_password ~* &* -@all +@read

# 创建读写用户(限制特定键模式)
ACL SETUSER readwrite on >readwrite_password ~app:* &* -@all +@read +@write

# 创建管理员用户
ACL SETUSER admin on >admin_password ~* &* +@all

# 创建应用用户(限制命令)
ACL SETUSER appuser on >app_password ~app:* &* -@all +get +set +del +exists +expire

# 删除用户
ACL DELUSER username

ACL 规则详解

# ACL 规则语法:
# on/off启用/禁用用户
# >password设置密码
# ~pattern允许访问的键模式
# &pattern允许访问的发布订阅频道模式
# +command允许的命令
# -command禁止的命令
# +@category允许的命令分类
# -@category禁止的命令分类

# 常用命令分类:
# @read读命令
# @write写命令
# @admin管理命令
# @dangerous危险命令
# @keyspace键空间命令
# @string字符串命令
# @list列表命令
# @set集合命令
# @hash哈希命令
# @sortedset有序集合命令

权限控制

细粒度权限配置

# 数据库管理员
ACL SETUSER dba on >dba_password ~* &* +@all

# 应用开发者
ACL SETUSER developer on >dev_password ~dev:* &dev:* -@all +@read +@write -flushdb -flushall -shutdown

# 监控用户
ACL SETUSER monitor on >monitor_password ~* &* -@all +info +ping +client +config|get

# 备份用户
ACL SETUSER backup on >backup_password ~* &* -@all +@read +bgsave +lastsave

# 只读分析用户
ACL SETUSER analyst on >analyst_password ~analytics:* &* -@all +@read +scan +keys

权限验证测试

# 测试用户权限
redis-cli --user readonly --pass readonly_password
127.0.0.1:6379> GET some_key  # 应该成功
127.0.0.1:6379> SET some_key value  # 应该失败

# 测试键模式限制
redis-cli --user developer --pass dev_password
127.0.0.1:6379> GET dev:config  # 应该成功
127.0.0.1:6379> GET prod:config  # 应该失败

IP 白名单

网络访问控制

# 绑定特定IP地址
# /etc/redis/redis.conf
bind 127.0.0.1 192.168.1.100 10.0.0.50

# 禁用保护模式(仅在安全网络环境中)
protected-mode no

# 使用防火墙限制访问
sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='192.168.1.0/24' port protocol='tcp' port='6379' accept"
sudo firewall-cmd --reload

网络安全

端口安全

端口配置和保护

# 更改默认端口
# /etc/redis/redis.conf
port 16379  # 使用非标准端口

# 禁用端口仅使用Unix套接字
port 0
unixsocket /var/run/redis/redis.sock
unixsocketperm 700

# 连接Unix套接字
redis-cli -s /var/run/redis/redis.sock

网络接口绑定

# 仅绑定内网接口
bind 127.0.0.1 192.168.1.100

# 绑定多个接口
bind 127.0.0.1 10.0.0.100 172.16.0.100

# 监听所有接口(不推荐)
# bind 0.0.0.0

SSL/TLS 加密

Redis 6.0+ 支持 SSL/TLS 加密传输。

生成SSL证书

# 创建证书目录
sudo mkdir -p /etc/redis/ssl
cd /etc/redis/ssl

# 生成私钥
sudo openssl genrsa -out redis.key 2048

# 生成证书签名请求
sudo openssl req -new -key redis.key -out redis.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Company/CN=redis.example.com"

# 生成自签名证书
sudo openssl x509 -req -days 365 -in redis.csr -signkey redis.key -out redis.crt

# 生成DH参数文件
sudo openssl dhparam -out redis.dh 2048

# 设置权限
sudo chown redis:redis /etc/redis/ssl/*
sudo chmod 600 /etc/redis/ssl/redis.key
sudo chmod 644 /etc/redis/ssl/redis.crt
sudo chmod 644 /etc/redis/ssl/redis.dh

配置SSL/TLS

# Redis 配置文件
# /etc/redis/redis.conf

# 启用TLS端口
port 0
tls-port 6380

# 证书文件路径
tls-cert-file /etc/redis/ssl/redis.crt
tls-key-file /etc/redis/ssl/redis.key
tls-dh-params-file /etc/redis/ssl/redis.dh

# CA证书如果使用
# tls-ca-cert-file /etc/redis/ssl/ca.crt

# 客户端证书验证
tls-auth-clients yes

# TLS协议版本
tls-protocols "TLSv1.2 TLSv1.3"

# 密码套件
tls-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
tls-ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256

# 会话缓存
tls-session-caching no
tls-session-cache-size 5000
tls-session-cache-timeout 60

SSL客户端连接

# 使用SSL连接
redis-cli --tls --cert /etc/redis/ssl/client.crt --key /etc/redis/ssl/client.key --cacert /etc/redis/ssl/ca.crt -p 6380

# 跳过证书验证(仅测试环境)
redis-cli --tls --insecure -p 6380

防火墙配置

iptables 配置

# 允许特定IP访问Redis
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 6379 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 6379 -j DROP

# 限制连接频率
sudo iptables -A INPUT -p tcp --dport 6379 -m connlimit --connlimit-above 10 -j DROP
sudo iptables -A INPUT -p tcp --dport 6379 -m recent --set --name redis
sudo iptables -A INPUT -p tcp --dport 6379 -m recent --update --seconds 60 --hitcount 20 --name redis -j DROP

# 保存规则
sudo iptables-save > /etc/iptables/rules.v4

UFW 配置

# 启用UFW
sudo ufw enable

# 允许SSH
sudo ufw allow ssh

# 允许特定网络访问Redis
sudo ufw allow from 192.168.1.0/24 to any port 6379

# 拒绝其他Redis连接
sudo ufw deny 6379

# 查看规则
sudo ufw status numbered

VPN 访问

OpenVPN 配置示例

# 安装OpenVPN
sudo apt update
sudo apt install openvpn easy-rsa

# 配置VPN服务器
sudo make-cadir /etc/openvpn/easy-rsa
cd /etc/openvpn/easy-rsa

# 初始化PKI
./easyrsa init-pki
./easyrsa build-ca
./easyrsa gen-req server nopass
./easyrsa sign-req server server
./easyrsa gen-dh

# 生成客户端证书
./easyrsa gen-req client1 nopass
./easyrsa sign-req client client1

数据安全

数据加密

应用层加密

# Python 数据加密示例
import redis
import json
from cryptography.fernet import Fernet

class EncryptedRedis:
    def __init__(self, host='localhost', port=6379, password=None, key=None):
        self.redis = redis.Redis(host=host, port=port, password=password)
        self.cipher = Fernet(key or Fernet.generate_key())
    
    def set(self, name, value, ex=None):
        """加密存储数据"""
        if isinstance(value, dict):
            value = json.dumps(value)
        encrypted_value = self.cipher.encrypt(value.encode())
        return self.redis.set(name, encrypted_value, ex=ex)
    
    def get(self, name):
        """解密获取数据"""
        encrypted_value = self.redis.get(name)
        if encrypted_value:
            decrypted_value = self.cipher.decrypt(encrypted_value)
            return decrypted_value.decode()
        return None
    
    def hset(self, name, key, value):
        """加密存储哈希字段"""
        if isinstance(value, dict):
            value = json.dumps(value)
        encrypted_value = self.cipher.encrypt(value.encode())
        return self.redis.hset(name, key, encrypted_value)
    
    def hget(self, name, key):
        """解密获取哈希字段"""
        encrypted_value = self.redis.hget(name, key)
        if encrypted_value:
            decrypted_value = self.cipher.decrypt(encrypted_value)
            return decrypted_value.decode()
        return None

# 使用示例
key = Fernet.generate_key()
encrypted_redis = EncryptedRedis(password='your_password', key=key)

# 存储加密数据
user_data = {'name': 'John', 'email': 'john@example.com', 'phone': '123-456-7890'}
encrypted_redis.set('user:1', json.dumps(user_data))

# 获取解密数据
data = encrypted_redis.get('user:1')
user_info = json.loads(data)
print(user_info)

敏感数据处理

敏感数据脱敏

# 数据脱敏工具
import re
import hashlib

class DataMasking:
    @staticmethod
    def mask_phone(phone):
        """手机号脱敏"""
        if len(phone) == 11:
            return phone[:3] + '****' + phone[7:]
        return phone
    
    @staticmethod
    def mask_email(email):
        """邮箱脱敏"""
        if '@' in email:
            local, domain = email.split('@')
            if len(local) > 2:
                masked_local = local[0] + '*' * (len(local) - 2) + local[-1]
            else:
                masked_local = '*' * len(local)
            return f"{masked_local}@{domain}"
        return email
    
    @staticmethod
    def mask_id_card(id_card):
        """身份证脱敏"""
        if len(id_card) == 18:
            return id_card[:6] + '********' + id_card[14:]
        return id_card
    
    @staticmethod
    def hash_sensitive_data(data, salt=''):
        """敏感数据哈希"""
        return hashlib.sha256((str(data) + salt).encode()).hexdigest()

# 使用示例
masker = DataMasking()

# 存储脱敏数据
user_data = {
    'name': 'John Doe',
    'phone': masker.mask_phone('13812345678'),
    'email': masker.mask_email('john.doe@example.com'),
    'id_card': masker.mask_id_card('110101199001011234')
}

redis_client = redis.Redis(password='your_password')
redis_client.hset('user:masked:1', mapping=user_data)

备份安全

安全备份策略

#!/bin/bash
# 安全备份脚本

BACKUP_DIR="/secure/backup/redis"
ENCRYPTION_KEY="/secure/keys/backup.key"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="redis_backup_$DATE.rdb"
ENCRYPTED_FILE="$BACKUP_FILE.enc"

# 创建备份目录
mkdir -p $BACKUP_DIR

# 生成备份
redis-cli --rdb $BACKUP_DIR/$BACKUP_FILE

# 加密备份文件
openssl enc -aes-256-cbc -salt -in $BACKUP_DIR/$BACKUP_FILE -out $BACKUP_DIR/$ENCRYPTED_FILE -pass file:$ENCRYPTION_KEY

# 删除未加密文件
rm $BACKUP_DIR/$BACKUP_FILE

# 计算校验和
sha256sum $BACKUP_DIR/$ENCRYPTED_FILE > $BACKUP_DIR/$ENCRYPTED_FILE.sha256

# 设置权限
chmod 600 $BACKUP_DIR/$ENCRYPTED_FILE
chown backup:backup $BACKUP_DIR/$ENCRYPTED_FILE

echo "备份完成:$BACKUP_DIR/$ENCRYPTED_FILE"

# 清理旧备份保留30天
find $BACKUP_DIR -name "*.enc" -mtime +30 -delete
find $BACKUP_DIR -name "*.sha256" -mtime +30 -delete

备份恢复脚本

#!/bin/bash
# 安全恢复脚本

BACKUP_FILE="$1"
ENCRYPTION_KEY="/secure/keys/backup.key"
TEMP_DIR="/tmp/redis_restore"

if [ -z "$BACKUP_FILE" ]; then
    echo "用法: $0 <encrypted_backup_file>"
    exit 1
fi

# 验证文件存在
if [ ! -f "$BACKUP_FILE" ]; then
    echo "备份文件不存在: $BACKUP_FILE"
    exit 1
fi

# 验证校验和
if [ -f "$BACKUP_FILE.sha256" ]; then
    echo "验证文件完整性..."
    sha256sum -c "$BACKUP_FILE.sha256"
    if [ $? -ne 0 ]; then
        echo "文件完整性验证失败"
        exit 1
    fi
fi

# 创建临时目录
mkdir -p $TEMP_DIR

# 解密备份文件
echo "解密备份文件..."
DECRYPTED_FILE="$TEMP_DIR/$(basename $BACKUP_FILE .enc)"
openssl enc -aes-256-cbc -d -in "$BACKUP_FILE" -out "$DECRYPTED_FILE" -pass file:$ENCRYPTION_KEY

if [ $? -eq 0 ]; then
    echo "备份文件已解密到: $DECRYPTED_FILE"
    echo "请手动将文件复制到Redis数据目录并重启服务"
else
    echo "解密失败"
    rm -rf $TEMP_DIR
    exit 1
fi

审计日志

审计日志配置

# Redis 配置文件
# /etc/redis/redis.conf

# 启用命令日志
logfile /var/log/redis/redis-server.log
loglevel notice

# 启用慢查询日志
slowlog-log-slower-than 10000
slowlog-max-len 128

# 客户端连接日志
# 通过监控脚本实现

审计日志脚本

#!/bin/bash
# Redis 审计日志脚本

LOG_FILE="/var/log/redis/audit.log"
REDIS_LOG="/var/log/redis/redis-server.log"

# 监控Redis连接
tail -f $REDIS_LOG | while read line; do
    if echo "$line" | grep -q "Accepted\|Client closed connection"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') $line" >> $LOG_FILE
    fi
done &

# 监控命令执行需要启用monitor
redis-cli monitor | while read line; do
    # 过滤敏感命令
    if echo "$line" | grep -qE "AUTH|CONFIG|EVAL|FLUSHDB|FLUSHALL|SHUTDOWN"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') SENSITIVE: $line" >> $LOG_FILE
    fi
done &

echo "审计日志监控已启动"

安全最佳实践

安全配置检查

安全配置检查清单

#!/bin/bash
# Redis 安全配置检查脚本

echo "=== Redis 安全配置检查 ==="

# 检查密码配置
echo "1. 检查密码配置"
if redis-cli CONFIG GET requirepass | grep -q "requirepass"; then
    echo "✓ 已配置密码认证"
else
    echo "✗ 未配置密码认证"
fi

# 检查绑定地址
echo "2. 检查绑定地址"
BIND_ADDR=$(redis-cli CONFIG GET bind | tail -1)
if [ "$BIND_ADDR" != "" ] && [ "$BIND_ADDR" != "0.0.0.0" ]; then
    echo "✓ 绑定地址配置安全: $BIND_ADDR"
else
    echo "✗ 绑定地址不安全: $BIND_ADDR"
fi

# 检查保护模式
echo "3. 检查保护模式"
PROTECTED_MODE=$(redis-cli CONFIG GET protected-mode | tail -1)
if [ "$PROTECTED_MODE" = "yes" ]; then
    echo "✓ 保护模式已启用"
else
    echo "✗ 保护模式未启用"
fi

# 检查危险命令
echo "4. 检查危险命令"
DANGEROUS_COMMANDS=("FLUSHDB" "FLUSHALL" "CONFIG" "EVAL" "SHUTDOWN" "DEBUG")
for cmd in "${DANGEROUS_COMMANDS[@]}"; do
    if redis-cli CONFIG GET "rename-command" | grep -q "$cmd"; then
        echo "✓ 危险命令 $cmd 已重命名或禁用"
    else
        echo "✗ 危险命令 $cmd 未处理"
    fi
done

# 检查文件权限
echo "5. 检查文件权限"
REDIS_CONF="/etc/redis/redis.conf"
if [ -f "$REDIS_CONF" ]; then
    PERM=$(stat -c "%a" "$REDIS_CONF")
    if [ "$PERM" = "640" ] || [ "$PERM" = "600" ]; then
        echo "✓ 配置文件权限安全: $PERM"
    else
        echo "✗ 配置文件权限不安全: $PERM"
    fi
fi

# 检查日志配置
echo "6. 检查日志配置"
LOGFILE=$(redis-cli CONFIG GET logfile | tail -1)
if [ "$LOGFILE" != "" ]; then
    echo "✓ 已配置日志文件: $LOGFILE"
else
    echo "✗ 未配置日志文件"
fi

echo "=== 检查完成 ==="

漏洞防护

常见漏洞防护措施

# 1. 禁用或重命名危险命令
# /etc/redis/redis.conf
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG "CONFIG_b840fc02d524045429941cc15f59e41cb7be6c52"
rename-command EVAL ""
rename-command DEBUG ""
rename-command SHUTDOWN "SHUTDOWN_b840fc02d524045429941cc15f59e41cb7be6c52"

# 2. 限制客户端连接
maxclients 1000
timeout 300
tcp-keepalive 300

# 3. 禁用Lua脚本调试
lua-replicate-commands yes

# 4. 设置内存限制
maxmemory 2gb
maxmemory-policy allkeys-lru

安全更新

安全更新策略

#!/bin/bash
# Redis 安全更新脚本

echo "检查Redis版本和安全更新"

# 获取当前版本
CURRENT_VERSION=$(redis-server --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
echo "当前Redis版本: $CURRENT_VERSION"

# 检查是否有安全更新
echo "检查安全公告..."
echo "请访问以下链接查看最新安全公告:"
echo "- https://redis.io/topics/security"
echo "- https://github.com/redis/redis/security/advisories"

# 备份当前配置
echo "备份当前配置..."
cp /etc/redis/redis.conf /etc/redis/redis.conf.backup.$(date +%Y%m%d)

# 更新前检查
echo "更新前安全检查:"
redis-cli CONFIG GET '*' > /tmp/redis_config_before_update.txt

echo "请手动执行以下步骤:"
echo "1. 下载最新稳定版本"
echo "2. 测试环境验证"
echo "3. 制定回滚计划"
echo "4. 执行更新"
echo "5. 验证功能和安全配置"

应急响应

安全事件应急响应

#!/bin/bash
# Redis 安全事件应急响应脚本

INCIDENT_LOG="/var/log/redis/security_incident.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

echo "=== Redis 安全事件应急响应 ==="
echo "事件时间: $DATE" | tee -a $INCIDENT_LOG

# 1. 立即隔离
echo "1. 立即隔离Redis服务"
echo "停止Redis服务? (y/n)"
read -r response
if [ "$response" = "y" ]; then
    sudo systemctl stop redis
    echo "$DATE: Redis服务已停止" | tee -a $INCIDENT_LOG
fi

# 2. 收集证据
echo "2. 收集安全证据"
EVIDENCE_DIR="/tmp/redis_incident_$(date +%Y%m%d_%H%M%S)"
mkdir -p $EVIDENCE_DIR

# 收集日志
cp /var/log/redis/* $EVIDENCE_DIR/ 2>/dev/null

# 收集配置
cp /etc/redis/redis.conf $EVIDENCE_DIR/

# 收集进程信息
ps aux | grep redis > $EVIDENCE_DIR/processes.txt

# 收集网络连接
netstat -tulpn | grep :6379 > $EVIDENCE_DIR/connections.txt

# 收集系统信息
uname -a > $EVIDENCE_DIR/system_info.txt
whoami > $EVIDENCE_DIR/current_user.txt

echo "证据已收集到: $EVIDENCE_DIR"
echo "$DATE: 证据收集完成 - $EVIDENCE_DIR" | tee -a $INCIDENT_LOG

# 3. 分析威胁
echo "3. 分析潜在威胁"
echo "检查可疑连接..."
redis-cli CLIENT LIST > $EVIDENCE_DIR/client_list.txt 2>/dev/null

echo "检查慢查询日志..."
redis-cli SLOWLOG GET 100 > $EVIDENCE_DIR/slowlog.txt 2>/dev/null

# 4. 修复建议
echo "4. 安全修复建议:"
echo "- 更改Redis密码"
echo "- 检查ACL配置"
echo "- 更新防火墙规则"
echo "- 检查系统用户账户"
echo "- 扫描恶意软件"
echo "- 更新Redis到最新版本"

echo "应急响应完成,详细日志: $INCIDENT_LOG"