一、漏洞描述
Redis Labs Redis是美国Redis Labs公司的一套开源的使用ANSI C编写、支持网络、可基于内存亦可持久化的日志型、键值(Key-Value)存储数据库,并提供多种语言的API。
Redis 7.0.0 到 7.0.10版本、6.2.0 到 6.2.11版本、6.0.0 到 6.0.18版本存在安全漏洞,该漏洞源于对用户提供的输入的验证不充分,攻击者利用该漏洞可以使用 HINCRBYFLOAT命令创建一个无效的哈希字段,这将使 Redis 在访问时崩溃,导致合法用户不能够访问正常服务;
影响版本:
7 <= Redis < 7.0.11
6.2 <= Redis < 6.2.12
Redis < 6.0.19
现场版本:6.2.7,需要升级到6.2.13
漏洞链接: https://access.redhat.com/security/cve/cve-2023-28856
官方补丁:https://github.com/redis/redis/,https://github.com/redis/redis/pull/11149,
https://github.com/redis/redis/commit/c924ac3fdf8fe544891dc66c88018e259ee4be87
修复建议:厂商已发布了漏洞修复程序,请使用此产品的用户尽快更新至安全版本:https://redis.io/,官网安装文档
二、处理过程
2.1、升级前准备
#版本检查
redis-server -v
#介质下载
wget https://download.redis.io/releases/redis-6.2.13.tar.gz
#或
wget https://download.redis.io/redis-stable.tar.gz
#或补丁修复
#src/t_hash.c
unsigned int vlen;
if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
#增加以下4行
if (isnan(incr) || isinf(incr)) {
addReplyError(c,"value is NaN or Infinity");
return;
}
#到这里
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {
if (vstr) {
#tests/unit/type/hash.tcl
assert {[r hincrbyfloat myhash float -0.1] eq {1.9}}
}
}
#增加如下4行
test {HINCRBYFLOAT does not allow NaN or Infinity} {
assert_error "*value is NaN or Infinity*" {r hincrbyfloat hfoo field +inf}
assert_equal 0 [r exists hfoo]
}
备份:使用redis自带工具备份和导出 Redis 数据参考如下:
SAVE: 阻塞所有客户端,直到 RDB 文件创建完毕。SAVE 命令将数据集持久化到硬盘上,阻塞 Redis 服务器进程直到快照文件创建完毕。如果正在使用 AOF 持久化,SAVE 命令将被忽略。
BGSAVE: 在后台异步保存 RDB 快照。Redis 会 fork 出一个新的子进程进行快照的生成工作,当前进程继续处理新的命令请求,不会被阻塞。在自动触发RDB持久时,Redis会选择bgsave而不是save来进行持久化;在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点;执行shutdown命令时,自动执行rdb持久化;redis被kill则不会触发备份机制。
#rdb和aof开启验证
#rdb开启配置
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir ./
rdbcompression yes #Redis采用LZF压缩方式,默认启用
#AOF开启
appendonly yes
appendfilename "appendonly.aof"
aof-load-truncated yes #是否忽略最后一条可能存在问题的指令
#或执行:
grep -Ev "^#|^$" redis.conf|grep -Ei "aof|rdb" //输出如下,可知现场同时开启了rdb和aof
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
appendfilename "appendonly.aof"
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes # RDB-AOF 混合持久化模式开启了
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
ll ./data/ //输出如下
total 44536028
-rw-r--r-- 1 root root 34872627978 Jun 28 09:32 appendonly.aof
-rw-r--r-- 1 root root 10732234315 Jun 28 09:48 dump.rdb
-rw-r--r-- 1 root root 129 Dec 23 2022 nodes-7379.conf
#备份rdb和aof文件
redis> config get dir //确认数据文件目录
redis-cli --rdb "dump.rdb" #方式1
redis-cli save #方式2
redis-cli bgsave #方式3
redis-cli save >> /data1/redis-6.2.7/dump_`date +%Y%m%d`.rdb #方式4
cp -pr ./data/ ./data_20230808
#现场为了方便,整体redis打包备份
cd /data1/
tar -czf redis627.tar.gz /data1/redis-6.2.7
#备份redis二进制文件
#二进制文件检查
ll /usr/local/bin/redis* //输出如下
-rwxr-xr-x 1 root root 4830144 Dec 23 2022 /usr/local/bin/redis-benchmark
lrwxrwxrwx 1 root root 12 Dec 23 2022 /usr/local/bin/redis-check-aof -> redis-server
lrwxrwxrwx 1 root root 12 Dec 23 2022 /usr/local/bin/redis-check-rdb -> redis-server
-rwxr-xr-x 1 root root 5004264 Dec 23 2022 /usr/local/bin/redis-cli
lrwxrwxrwx 1 root root 12 Dec 23 2022 /usr/local/bin/redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 9536000 Dec 23 2022 /usr/local/bin/redis-server
-rwxr-xr-x 1 root root 3600 Dec 23 2022 /usr/local/bin/redis-trib.rb
mv redis-server redis-server_6.2.7
mkdir -p /data1/redis_backup
mv /usr/local/bin/redis* /data1/redis_backup
2.2、编译安装
#关停redis
redis-cli shutdown
#新的redis目录
cd /data1
tar -xzvf redis-6.2.13.tar.gz
cd redis-6.2.13
make
cp src/redis-benchmark /usr/local/bin/
cp src/redis-check-aof /usr/local/bin/ #注意软连接
cp src/redis-check-dump /usr/local/bin/
cp src/redis-cli /usr/local/bin/
cp src/redis-sentinel /usr/local/bin/ #注意软连接
cp src/redis-server /usr/local/bin/
cp src/redis-check-rdb /usr/local/bin/ #注意软连接
cp src/redis-trib.rb /usr/local/bin/
//二进制替换旧不用执行如下安装命令了
make install
chmod 755 /usr/local/bin/redis*
#二进制文件替换
#重启服务
mv /data1/redis-6.2.7/ /data1/redis-6.2.13/
/usr/local/redis-server /data1/redis-6.2.13/redis.conf
注:启动新版本的 Redis 服务后,需要将原来的 Redis 数据导入到新版本的 Redis 中。可以使用 Redis 的数据同步命令,将原来的数据同步到新的 Redis 中。Redis 数据同步命令常用的有两个:
slaveof:将 Redis 服务设置为主从模式,将原来的 Redis 服务设置为主服务器,新版本的 Redis 服务设置为从服务器。
sync:将从服务器上的数据库与主服务器进行同步。 示例命令:
redis-cli -h oldredis_ip -p oldredis_port slaveof newredis_ip 6380
redis-cli sync //新版本中执行同步命令
redis-benchmark -h newredis_ip -p 6380 -n 10000 -c 5 //同步完成后,测试确保数据完整性和服务可用性
2.3、验证
#连接 redis 客户端:
./redis-cli -p 6379 -h ip
#查看版本命令
ip:port>info server //输出版本
#或
./redis-cli -p 6379 -h ip info
#或
redis-cli --version
redis-server--version
另平滑升级可参考:平滑升级
三、附录
3.1、Redis持久化
Redis将数据存储在内存中,宕机或重启都会使内存数据全部丢失, Redis的持久化机制用来保证数据不会因为故障而丢失。Redis提供两种持久化方式:
RDB(内存快照方式):生成rdb文件,rdb是某一时间点内存数据的全量备份,文件内容是存储结构非常紧凑的二进制序列化形式;主要用于:缓存和数据迁移备份场景
优点:
全量快照,数据与内存基本是一致的。
占用空间相对比较小。
快照是二进制文件,生成、恢复都比较快。
缺点:
丢失数据的概率相对有点大。注意:SHUTDOWN 和 FLUSHALL (清空)命令都会触发RDB快照
配置参考:
语法:save <seconds> <changes>#save “”
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir ./
rdbcompression yes #Redis采用LZF压缩方式
AOF(日志备份方式) :日志保存的是基于数据的连续增量备份,日志文件内容是服务端执行的每一条指令的记录文本。
解发机制:
always:内存更新一条,即往磁盘追加一条。
everysec:每1s往磁盘追加一次。
no:同步的操作交给操作系统来完成。
优点:
直接追加,同步开销相对较小,可根据需求自由选择安全和性能。
.缺点:
日志文件是普通文本文件,恢复相对较慢。此外,日志文件中会包含很多无用操作。
当 aof-use-rdb-preamble 设置为 yes 时,表示开启 RDB-AOF 混合持久化模式;在该模式下,AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容,该文件的前半段是 RDB 格式的全量数据,而后半段是 Redis 命令格式的增量数据;
Redis持久化策略选用参考:
如果数据不能丢失,RDB和AOF混用。---->常用
如果只作为缓存使用,可以接受部分数据的丢失,可以使用RDB。
如果只使用AOF,优先使用everysec的写回策略。
3.2、创建redis自启动服务
vi /etc/systemd/system/redis@.service //参考如下
[Unit]
Description=The redis-server Process lanager
After=syslog. target
After"network. target
[Service]
Typeforking
ExecStart=/usr/1ocal/bin/redis-server /data1/redis-6.2.13/redis.conf
ExecStop=/usr/1ocal/bin/redis-cli -p%i shutdown
Restart=always
PrivateTmptrue
[Instal1]
WantedBy=multi-user.target
#配置
systemctl enable --now redis@{7000, 7001}.service
systemctl status redis@7000.service
systemctl start redis@7000.service
3.3、Redis升级脚本
#!/bin/bash
# 备份 Redis 数据
echo "Backup Redis data..."
redis-cli bgsave
cd /data1
redis-cli shutdown
mv /data1/redis-6.2.7 /data1/redis-back_`date +%Y%m%d`
# 下载 Redis 新版本
echo "Downloading Redis new version..."
wget -q https://download.redis.io/releases/redis-6.2.13.tar.gz
# 解压 Redis 安装包
echo "Extracting Redis package..."
tar xzf redis-6.2.13.tar.gz
cd redis-6.2.13
# 编译 Redis
echo "Compiling Redis..."
make
# 安装 Redis
echo "Installing Redis..."
make install
# 启动新版本 Redis
echo "Starting Redis new version..."
redis-server /path/to/new/redis.conf --port 6380 &
# 数据同步
echo "Synchronizing Redis data..."
redis-cli -h old_redis_host -p old_redis_port slaveof new_redis_host 6380
redis-cli sync
# 测试 Redis
echo "Testing Redis..."
redis-benchmark -h new_redis_host -p 6380 -n 10000 -c 5