大家应该都很熟悉Redis相关的安全漏洞了,比如未授权啊,写shell啊 ,写计划任务,主从复制之类的。
那么这篇文章是想对自己也是对于光知道将命令输上去看有没有写成功或者反弹shell成功的那些人来说更巩固一些。
那么我们就从0开始吧。漏洞相关可以看(持久化配置)
为什么企业中需要Redis?
Redis是一个非关系型数据库。他是使用key=>value的方式进行存储的。
就比如说我们存储一组账号密码。
我们可以使用 set name value 这样的方式进行存储。但是这是最基本的存储。
在一些高并发访问量比较高的一些网站来说,就需要考虑redis了。
因为redis中的所有数据都存储在内存中。(这就说明了,内存不值钱) 由于内存的读写速度远快于硬盘,因此Redis的的的在性能上对比其他基于硬盘存储的数据库有非常明显的优势。
Redis是基于内存操作的,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽。
简单来说就是读取速度快。
Redis中有多少个数据库?
Redis中有16个数据库,后面我们会通过配置文件得知。也就是redis.conf。
也可以通过select命令来进行切换数据库。也就是可以切换 0-15 个数据库
我们在数据库1中,写入一个键值对,k1代表key v1 代表value,就好比跟字典一样,我们可以通过key来进行获取value。
为什么Redis的端口是6379?
参考这篇文章:https://xxhjs.nuc.edu.cn/info/1010/3665.htm 感觉挺有意思的。
Redis的数据类型
这个数据类型相对来说是比较多的。
我们分为基本数据库为:
String 字符串
List 列表
Set Set类型
Hash Hash类型
Zset Zset类型
三种特殊的数据类型
geospatial:这个类型的应用场景为:朋友的定位 附近的人 打车距离计算
Hyperloglog:传统的方式 set保存用户的id
Bitmaps: 位存储
统计用户信息 活跃 不活跃 登录 未登录!两个状态的
Bitmaps 位图 数据结构 都是操作二进制位来进行记录 就只有0和1两个状态
我们前面得知redis存储的数据都是通过key和value进行存储的。
那么这些类型也是一样的。
这里我们只做前面两种类型的演示。其他可以参考官网
在列举这些类型之前我们把redis常用的几个命令来说一下:
清除当前数据库 flushdb
127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379[3]>
清除全部数据的内容 FLUSHALL
127.0.0.1:6379> set name admin
OK
127.0.0.1:6379>
127.0.0.1:6379>
127.0.0.1:6379>
127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379[3]> flushALL
OK
127.0.0.1:6379[3]> select 0
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>
判断这个key是否存在 EXISTS name
127.0.0.1:6379> EXISTS name
(integer) 1
设置name这个key 10秒后过期 expire name 10
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> get name
"admin"
127.0.0.1:6379> get name
(nil)
查看name是什么类型的数据type name
127.0.0.1:6379> set name admin
OK
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379> type name
string
127.0.0.1:6379> type age
string
127.0.0.1:6379>
以上是redis的一些基本常用的一些命令。
String类型:
0 set views
OK
KEYS *
1) "views"
#对views这个key进行加1操作 相当于i++ INCR views
(integer) 1
KEYS *
1) "views"
KEYS *
1) "views"
get views
"1"
INCR views
(integer) 2
get views
"2"
#对views这个key进行减1操作 相当于 i-- DECR views
(integer) 1
get views
"1"
10 #对views这个key进行加10操作 INCRBY views
(integer) 11
10 #对views这个key进行减10操作 DECRBY views
############################################################################################################
#字符串的范围 range
1 2 #截取字符串 从0开始的 GETRANGE key
"dm"
0 2 GETRANGE key
"adm"
0 -1 #截取所有的字符串 和 get key是一样的 GETRANGE key
"admin"
#替换
1 xx #替换指定位置的字符串 SETRANGE key2
(integer) 5
get key2
"axxce"
##########################################################################################
#setex(set with expire) #设置过期时间
#setnx(set if not exist) #不存在设置
FLUSHALL
OK
10 "hello" #创建一个key 10秒后过期 SETEX key
OK
tll key
(error) ERR unknown command 'tll', with args beginning with: 'key'
ttl key
(integer) -2
"redis" SETNX key
(integer) 1
KEYS *
1) "key"
"redis" #判断这个key是否存在 如果不存在则创建 如果存在则创建失败 SETNX key
(integer) 0
KEYS *
1) "key"
#######################################################################
mset
mget
#同时设置多个值 MSET k1 v1 key1 v2
OK
keys *
1) "k1"
2) "key1"
mget k1 k2
1) "v1"
2) (nil)
#同时获取多个值 mget k1 key2
1) "v1"
2) (nil)
mget k1 key1
1) "v1"
2) "v2"
mset k1 v1 k4 v4
OK
KEYS *
1) "k1"
2) "k4"
3) "key1"
#######################################################################
#对象
set user:1{name:zhangsan,age:3} #设置一个user1对象 值为json字符串来保存一个对象
#这里的key是一个巧妙的设计 user:{id}:{field} 如此设计在Redis是完全可以的
user:1:name zhangsan user:1:age 2 MSET
OK
user:1:name MGET
1) "zhangsan"
user:1:age MGET
1) "2"
KEYS *
1) "user:1:age"
2) "key1"
3) "k4"
4) "k1"
5) "user:1:name"
#######################################################################
getset #先get然后再set
#如果不存在值返回nil GETSET db redis
(nil)
get db
"redis"
#如果存在值 先获取原来的值 并设置新的值w getset db momgodb
"redis"
get db
"momgodb"
List类型
127.0.0.1:6379> LPUSH list one #将一个值或者多个值插入到列表的头部
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 01
(error) ERR value is not an integer or out of range
127.0.0.1:6379> LRANGE list 0 -1 #获取list中的所有值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
127.0.0.1:6379>
127.0.0.1:6379> RPUSH list rigrsh #将一个值或者多个值插入到列表的尾部
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "rigrsh"
#######################################################################
LPOP
RPOP
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "rigrsh"
127.0.0.1:6379> LPOP list #移除list的第一个元素
"three"
127.0.0.1:6379> rPOP list #移除list的最后一个元素
"rigrsh"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379>
#######################################################################
Lindex
127.0.0.1:6379> LINDEX list 0 #通过下标获得list中的某一个值
"two"
127.0.0.1:6379> LINDEX list 1
"one"
127.0.0.1:6379>
#######################################################################
Llen
127.0.0.1:6379> LPUSH list one
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LLEN list #返回列表的长度
(integer) 3
127.0.0.1:6379>
#######################################################################
移除指定的值!
取关
LREM
127.0.0.1:6379> LPUSH list one
(integer) 4
127.0.0.1:6379> LREM list 1 one
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LREM list 2 three #移除list列表中指定个数的value 精确匹配
(integer) 1
127.0.0.1:6379> LREM list 2 three
(integer) 0
127.0.0.1:6379> LREM list 2 three
(integer) 0
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379>
#######################################################################
trim 修剪 : list 截断
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello3"
2) "hello2"
3) "hello1"
4) "hello"
127.0.0.1:6379> LTRIM mylist 1 2 #截取指定的长度上的元素 这个list已经被改变了 只剩下截取的元素
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "hello2"
2) "hello1"
#######################################################################
rpoplpush #移除列表的最后一个元素 并将他移动到新的列表中
127.0.0.1:6379> RPUSH mylist "hello"
(integer) 1
127.0.0.1:6379> RPUSH mylist "hello1"
(integer) 2
127.0.0.1:6379> RPUSH mylist "hello2"
(integer) 3
127.0.0.1:6379> RPOPLPUSH mylist myotherlist #移除列表的最后一个元素 并将他移动到新的列表中
"hello2"
127.0.0.1:6379> LRANGE mylist 0 -1 #查看原来的列表
1) "hello"
2) "hello1"
127.0.0.1:6379> LRANGE myotherlist 0 -1 #查看目标列表中 确实存在改值
1) "hello2"
#######################################################################
lset 将列表中的指定下标的值替换为另外一个值,更新操作。
127.0.0.1:6379> EXISTS mylist #判断这个列表是否存在
(integer) 0
127.0.0.1:6379> EXISTS list
(integer) 0
127.0.0.1:6379> LSET list 0 item #如果不存在列表我们更新就会报错
(error) ERR no such key
127.0.0.1:6379> LPUSH list value1
(integer) 1
127.0.0.1:6379> LRANGE list 0 0
1) "value1"
127.0.0.1:6379> LRANGE list 0 0
1) "value1"
127.0.0.1:6379> LSET list 0 item #如果存在 更新当前下标的值
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "item"
#######################################################################
linsert
127.0.0.1:6379> lpush mylist hello
(integer) 1
127.0.0.1:6379> lpush mylist word
(integer) 2
127.0.0.1:6379> LINSERT mylist before "word" "other"
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "other"
2) "word"
3) "hello"
其他类型可以通过官网或者一些视频来跟着来做但是如果不做开发的也可以不了解。
SpringBoot整合Redis
接下来我们来看下在SpringBoot中如何操作Redis
首先添加配置文件
spring.redis.host=127.0.0.1spring.redis.port=6379
添加Pom依赖
dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
使用RedisTemplate来操作Redis数据库
package com.springboot;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springboot.pojo.User;
import com.springboot.utils.RedisUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
class RedisDemoApplicationTests {
private RedisTemplate redisTemplate;
private RedisUtil redisUtil;
void contextLoads() {
//在企业开发中 我们80%情况下 都不会使用这个原生的方式去编写代码
//redisTemplate
// opsForValue 类似String
// opsForList 类似于List
//opsForSet 类似于set
//opsForHash 类似于hash
//opsForGeo 操作Geo
//opsForZset 操作zset
//除了基本的操作 我们常用的方法 都可以直接通过redisTemplate来操作 比如事务基本的增删改查
//获取redis的连接对象
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushDb();
// connection.flushAll();
// connection.select(1);
redisTemplate.opsForValue().set("mykey","zhangsan");
System.out.println(redisTemplate.opsForValue().get("mykey"));
}
void RedisTest() throws JsonProcessingException {
//真实的开发一般都是用Json来传递对象
User user = new User(1, "张三", "admin123");
String jsonuser = new ObjectMapper().writeValueAsString(user);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
void test(){
redisUtil.set("mykey","zhangsan");
redisUtil.get("mykey");
}
}
Redis工具类:
package com.springboot.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public final class RedisUtil {
private RedisTemplate redisTemplate;
// =============================common============================
public Set<String> keys(String pattern) {
return redisTemplate.keys(pattern);
}
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
"unchecked") (
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public <T> T get(String key) {
if (key == null) return null;
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
return redisTemplate.opsForList().remove(key, count, value);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
Redis的配置文件
上面这些对于我们做安全来说其实意义不是很大。下面配置文件我们是需要了解的。
也就是我们的redis.conf,我们安装redis的时候会有这个配置文件。下面对redis.conf进行了解。
基本配置
bind 127.0.0.1 #指定绑定的ip 这里的IP表示只能本地连接
protected-mode yes #保护模式 这里如果设置保护模式,那么只允许我们在本机的回环连接,其他机器无法连接。
port 6379
daemonize yes #以守护进程的方式运行 默认是no 我们需要自己开启为yes 这里就相当于给后台一个进程
loglevel notice #日志
database 16 #数据库的数量 默认是16个数据库
always-show-logo yes #是否总是显示logo
我们可以看到上面默认的database为16个数据看来,以及bind绑定的ip等等。
持久化配置(重点)
持久化 在规定的时间内 执行了多少次操作 则会持久化到文件 .rdb .aof
redis是内存数据库如果没有持久化,那么数据就没有了。
这里是一个重点,因为如果我们拿shell的话就需要通过持久化将数据保存到硬盘进行存储。
#如果900秒内 如果至少有一个key进行了修改 我们就进行持久化操作
save 900 1
#如果300秒内 如果至少有10个key进行了修改 我们就进行持久化操作
save 300 10
#如果60秒内 如果至少有10000个key进行了修改 我们就进行持久化操作
save 60 10000
#我们之后虚席持久化 会自己定义测试!
rdbcompression yes #是否压缩rdb文件 需要消耗一些cpu资源
rdbchecksum yes # 保存rdb文件的时候 进行错误的检查校验
dir ./ #rdb文件保存的目录!
dbfilename dump.rdb #默认保存的文件
我们可以看到这里有一个dbfilename的一个配置,这里的配置表示我们存储的文件名以及后缀。
如果我们将dbfilename 改为了shell.php,name我们是不是就可以写入到目标站点的目录拿shell了。
包括我们的写入计划任务什么的都是通过持久化存储这个配置进行实现的。
这就是网上拿shell使用的 config dbfilename shell.php这条命令。然后进行save命令保存的,后面我们会演示到。
SECURITY 安全
可以在这里设置redis的密码 默认是没有密码!我们一定要记住在redis控制台配置的密码,他不是永久生效的。需要在配置文件中进行直接配置。
#获取redis的密码 config get requirepass
1) "requirepass"
2) ""
config get requirepass
1) "requirepass"
2) ""
123456"" #设置redis的密码 但是命令操作的都是临时的 config set requirepass
OK
get
(error) ERR wrong number of arguments for 'get' command
123456 #输入密码进行验证 auth
OK
config get requirepass
1) "requirepass"
2) "123456"
限制CLIENTS
maxclients 10000 #设置能连接上redis的最大客户端的数量
maxmemory <bytes> #redis配置最大的的内存容量
#移除一些过期的key
#报错
APPEND ONLY模式 aof配置
这个配置也是一个持久化存储的一个配置,这个应该也可以写shell的,但是他的文件太大了,并且他并不是默认开启的。
这个我没有测试过,大家也可以去测试一下。
appendonly no #默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下 rdb完全够用!
appendfilename "appendonly.aof" #持久化的文件的名字
持久化存储
Rdb持久化存储
触发机制
1.save的规则满足的情况下 会自动触发rdb规则
2.执行flushall命令 也会触发我们的rdb规则
3.退出redis 也会产生rdb文件!
备份就会自动生成一个dump.rdb
我们可以来试一下。他默认的存储路径是 dir /opt/homebrew/var/db/redis/
我这里是这个路径具体看你配置文件中的。可以看到这里执行save命令的时候他会生成dump.rdb 其他几个rdb文件我们不要管,他是后面做主从复制的时候需要用到的。
那么看到这里我们是不是可以将我们的dbfilename修改为dump.php呢?我们来试试。
127.0.0.1:6379> config set dbfilename zcc.php
(error) ERR CONFIG SET failed (possibly related to argument 'dbfilename') - can't set protected config
我们会发现他会报错为这是受保护的配置,我们无法修改,这是因为这是高版本的redis增加的。在低版本中是没有的。
我们需要将配置文件中的enable-protected-configs设置去掉前面的引号,更改为yes就可以修改配置文件了。
然后设置下dir的路径执行save命令就可以了。
Aof持久化存储
将我们的所有命令都记录下来 history 恢复的时候就把这个文件全部再执行一遍 这里也可以进行写入shell
默认是不开启的 我们需要手动进行开启 如果开启了这里应该也可以进行写入shell
appendonly yes
今天就到这里了,主从复制就不说了 有兴趣可以加我我给你发笔记。
Get__Post
原文始发于微信公众号(白帽子):Redis从0开始
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论