【实战】Nginx+Lua脚本+Redis 实现自动封禁访问频率过高IP
大家好,我是冰河~~
自己搭建的网站刚上线,短信接口就被一直攻击,并且攻击者不停变换IP,导致阿里云短信平台上的短信被恶意刷取了几千条,加上最近工作比较忙,就直接在OpenResty上对短信接口做了一些限制,采用OpenResty+Lua的方案成功动态封禁了频繁刷短信接口的IP。
一、临时解决方案
由于事情比较紧急,所以,当发现这个问题时,就先采用快速的临时方案解决。
(1)查看Nginx日志发现被攻击的IP 和接口
[root@binghe ~]# tail -f /var/log/nginx/access.log
发现攻击者一直在用POST请求 /fhtowers/user/getVerificationCode这个接口
(2)用awk和grep脚本过滤nginx日志,提取攻击短信接口的ip(一般这个接口是用来发注册验证码的,一分钟如果大于10次请求的话就不是正常的访问请求了,大家根据自己的实际情况更改脚本)并放到一个txt文件中去,然后重启nginx
[root@binghe ~]# cat denyip.sh
#!/bin/bash
nginx_home=/usr/local/openresty/nginx
log_path=/var/log/nginx/access.log
tail -n5000 $log_path | grep getVerification | awk '{print $1}' |sort | uniq -c | sort -nr -k1 | head -n 100 |awk '{if($1>10)print ""$2""}' >$nginx_home/denyip/blocksip.txt
/usr/bin/nginx -s reload
(3)设置Nginx去读取用脚本过滤出来的blocksip.txt(注意一下,我这里的Nginx是用的openresty,自带识别lua语法的,下面会有讲openresty的用法)
location = /fhtowers/user/getVerificationCode { #短信接口
access_by_lua 'local f = io.open("/usr/local/openresty/nginx/denyip/blocksip.txt") #黑名单列表for line in f:lines() doif ngx.var.http_x_forwarded_for == line then #如果ip在黑名单列表里直接返回403ngx.exit(ngx.HTTP_FORBIDDEN)endend';proxy_pass http://appservers; #不在名单里就转发给后台的tomcat服务器
}
(4)把过滤脚本放进crontab任务里,一分钟执行一次
[root@binghe ~]# crontab -e
*/1 * * * * sh /root/denyip.sh
(5)查看一下效果,发现攻击者的请求都被返回403并拒绝了
二、OpenResty+Lua方案
临时方案有效果后,再将其调整成使用OpenResty+Lua脚本的方案,来一张草图。
接下来,就是基于OpenResty和Redis实现自动封禁访问频率过高的IP。
2.1 安装OpenResty
安装使用 OpenResty,这是一个集成了各种 Lua 模块的 Nginx 服务器,是一个以Nginx为核心同时包含很多第三方模块的Web应用服务器,使用Nginx的同时又能使用lua等模块实现复杂的控制。
(1)安装编译工具、依赖库
[root@test1 ~]# yum -y install readline-devel pcre-devel openssl-devel gcc
(2)下载openresty-1.13.6.1.tar.gz 源码包,并解压;下载ngx_cache_purge模块,该模块用于清理nginx缓存;下载nginx_upstream_check_module模块,该模块用于ustream健康检查。
[root@test1 ~]# cd /usr/local/
[root@test1 local]# wget https://openresty.org/download/openresty-1.13.6.1.tar.gz
[root@test1 local]# tar -zxvf openresty-1.13.6.1.tar.gz
[root@test1 local]# cd openresty-1.13.6.1/bundle
[root@test1 local]# wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
[root@test1 local]# tar -zxvf ngx_cache_purge-2.3.tar.gz
[root@test1 local]# wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz
[root@test1 local]# tar -zxvf v0.3.0.tar.gz
(3)配置需安装的模块
# ./configure --help可查询需要安装的模块并编译安装
[root@test1 openresty-1.13.6.1]# ./configure --prefix=/usr/local/openresty --with-luajit --with-http_ssl_module --user=root --group=root --with-http_realip_module --add-module=./bundle/ngx_cache_purge-2.3/ --add-module=./bundle/nginx_upstream_check_module-0.3.0/ --with-http_stub_status_module
[root@test1 openresty-1.13.6.1]# make && make install
(4)创建一个软链接方便启动停止
[root@test1 ~]# ln -s /usr/local/openresty/nginx/sbin/nginx /bin/nginx
(5)启动nginx
[root@test1 ~]# nginx #启动
[root@test1 ~]# nginx -s reload #reload配置
如果启动时候报错找不到PID的话就用以下命令解决(如果没有更改过目录的话,让它去读nginx的配置文件就好了)
[root@test1 ~]# /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.conf
随后,打开浏览器访问页面。
(6)在Nginx上测试一下能否使用Lua脚本
[root@test1 ~]# vim /usr/local/openresty/nginx/conf/nginx.conf
在server里面加一个
location /lua {default_type text/plain;content_by_lua ‘ngx.say(“hello,lua!”)’;
}
加完后重新reload配置。
[root@test1 ~]# nginx -s reload
在浏览器里输入 ip地址/lua,出现下面的字就表示Nginx能够成功使用lua了
2.2 安装Redis
(1)下载、解压、编译安装
[root@test1 ~]# cd /usr/local/
[root@test1 local]# wget http://download.redis.io/releases/redis-6.0.1.tar.gz
[root@test1 local]# tar -zxvf redis-6.0.1.tar.gz
[root@test1 local]# cd redis-6.0.1
[root@test1 redis-6.0.1]# make
[root@test1 redis-6.0.1]# make install
(2)查看是否安装成功
[root@test1 redis-6.0.1]# ls -lh /usr/local/bin/
[root@test1 redis-6.0.1]# redis-server -v
Redis server v=3.2.5 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=dae2abf3793b309d
(3)配置redis 创建dump file、进程pid、log目录
[root@test1 redis-6.0.1]# cd /etc/
[root@test1 etc]# mkdir redis
[root@test1 etc]# cd /var/
[root@test1 var]# mkdir redis
[root@test1 var]# cd redis/
[root@test1 redis]# mkdir data log run
(4)修改配置文件
[root@test1 redis]# cd /usr/local/redis-6.0.1/
[root@test1 redis-6.0.1]# cp redis.conf /etc/redis/6379.conf
[root@test1 redis-6.0.1]# vim /etc/redis/6379.conf
#绑定的主机地址
bind 192.168.1.222
#端口
port 6379
#认证密码(方便测试不设密码,注释掉)
#requirepass
#pid目录
pidfile /var/redis/run/redis_6379.pid
#log存储目录
logfile /var/redis/log/redis.log
#dump目录
dir /var/redis/data
#Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize yes
(5)设置启动方式
[root@test1 redis-6.0.1]# cd /usr/local/redis-6.0.1/utils/
[root@test1 utils]# cp redis_init_script /etc/init.d/redis
[root@test1 utils]# vim /etc/init.d/redis #根据自己实际情况修改
/etc/init.d/redis文件的内容如下。
#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.REDISPORT=6379
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cliPIDFILE=/var/run/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"case "$1" instart)if [ -f $PIDFILE ]thenecho "$PIDFILE exists, process is already running or crashed"elseecho "Starting Redis server..."$EXEC $CONFfi;;stop)if [ ! -f $PIDFILE ]thenecho "$PIDFILE does not exist, process is not running"elsePID=$(cat $PIDFILE)echo "Stopping ..."$CLIEXEC -p $REDISPORT shutdownwhile [ -x /proc/${PID} ]doecho "Waiting for Redis to shutdown ..."sleep 1doneecho "Redis stopped"fi;;*)echo "Please use start or stop as first argument";;
esac
增加执行权限,并启动Redis。
[root@test1 utils]# chmod a+x /etc/init.d/redis #增加执行权限
[root@test1 utils]# service redis start #启动redis
(6)查看redis是否启动
2.3 Lua访问Redis
(1)连接redis,然后添加一些测试参数
[root@test1 utils]# redis-cli -h 192.168.1.222 -p 6379
192.168.1.222:6379> set "123" "456"
OK
(2)编写连接Redis的Lua脚本
[root@test1 utils]# vim /usr/local/openresty/nginx/conf/lua/redis.lua
local redis = require "resty.redis"
local conn = redis.new()
conn.connect(conn, '192.168.1.222', '6379') #根据自己情况写ip和端口号
local res = conn:get("123")
if res==ngx.null thenngx.say("redis集群中不存在KEY——'123'")return
end
ngx.say(res)
(3)在nginx.conf配置文件中的server下添加以下location
[root@test1 utils]# vim /usr/local/openresty/nginx/conf/nginx.conf
location /lua_redis {default_type text/plain;content_by_lua_file /usr/local/openresty/nginx/conf/lua/redis.lua;
}
随后重新reload配置。
[root@test1 utils]# nginx -s reload #重启一下Nginx
(4)验证Lua访问Redis的正确性
在浏览器输入ip/lua_redis, 如果能看到下图的内容表示Lua可以访问Redis。
准备工作已经完成,现在要实现OpenResty+Lua+Redis自动封禁并解封IP了。3.4
2.4 OpenResty+Lua实现
(1)添加访问控制的Lua脚本(只需要修改Lua脚本中连接Redis的IP和端口即可)
ok, err = conn:connect(“192.168.1.222”, 6379)
注意:如果在Nginx或者OpenResty的上层有用到阿里云的SLB负载均衡的话,需要修改一下脚本里的所有…ngx.var.remote_addr,把remote_addr替换成从SLB获取真实IP的字段即可,不然获取到的IP全都是阿里云SLB发过来的并且是处理过的IP,同时,这些IP全都是一个网段的,根本没有办法起到封禁的效果)。
完整的Lua脚本如下所示。
[root@test1 lua]# vim /usr/local/openresty/nginx/conf/lua/access.lua
local ip_block_time=300 --封禁IP时间(秒)
local ip_time_out=30 --指定ip访问频率时间段(秒)
local ip_max_count=20 --指定ip访问频率计数最大值(秒)
local BUSINESS = ngx.var.business --nginx的location中定义的业务标识符,也可以不加,不过加了后方便区分--连接redis
local redis = require "resty.redis"
local conn = redis:new()
ok, err = conn:connect("192.168.1.222", 6379)
conn:set_timeout(2000) --超时时间2秒--如果连接失败,跳转到脚本结尾
if not ok thengoto FLAG
end--查询ip是否被禁止访问,如果存在则返回403错误代码
is_block, err = conn:get(BUSINESS.."-BLOCK-"..ngx.var.remote_addr)
if is_block == '1' thenngx.exit(403)goto FLAG
end--查询redis中保存的ip的计数器
ip_count, err = conn:get(BUSINESS.."-COUNT-"..ngx.var.remote_addr)if ip_count == ngx.null then --如果不存在,则将该IP存入redis,并将计数器设置为1、该KEY的超时时间为ip_time_outres, err = conn:set(BUSINESS.."-COUNT-"..ngx.var.remote_addr, 1)res, err = conn:expire(BUSINESS.."-COUNT-"..ngx.var.remote_addr, ip_time_out)
elseip_count = ip_count + 1 --存在则将单位时间内的访问次数加1if ip_count >= ip_max_count then --如果超过单位时间限制的访问次数,则添加限制访问标识,限制时间为ip_block_timeres, err = conn:set(BUSINESS.."-BLOCK-"..ngx.var.remote_addr, 1)res, err = conn:expire(BUSINESS.."-BLOCK-"..ngx.var.remote_addr, ip_block_time)elseres, err = conn:set(BUSINESS.."-COUNT-"..ngx.var.remote_addr,ip_count)res, err = conn:expire(BUSINESS.."-COUNT-"..ngx.var.remote_addr, ip_time_out)end
end-- 结束标记
::FLAG::
local ok, err = conn:close()
(2)在需要做访问限制的location里加两段代码即可,这里用刚才的/lua做演示
[root@test1 lua]# vim /usr/local/openresty/nginx/conf/nginx.conf
主要是添加如下配置。
access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;
其中,set $business “lua”
是为了把IP放进Redis的时候标明是哪个location的,可以不加这个配置。
随后,重新reload配置。
[root@test1 lua]# nginx -s reload #修改完后重启nginx
(3)打开浏览器访问192.168.1.222/lua 并一直按F5刷新。
随后,连接Redis,查看IP的访问计数。
[root@test1 ~]# redis-cli -h 192.168.1.222 -p 6379
发现redis已经在统计访问lua这个网页ip的访问次数了
这个key的过期时间是30秒,如果30秒没有重复访问20次这个key就会消失,所以说正常用户一般不会触发这个封禁的脚本。
当30秒内访问超过了20次,发现触发脚本了,变成了403
再次查看Redis的key,发现多了一个lua-block-192.168.1.158,过期时间是300秒,就是说在300秒内这个ip无法继续访问192.168.1.222/lua这个页面了。
过五分钟后再去访问这个页面,又可以访问了。
这个脚本的目的很简单:一个IP如果在30秒内其访问次数达到20次则表明该IP访问频率太快了,因此将该IP封禁5分钟。同时由于计数的KEY在Redis中的超时时间设置成了30秒,所以如果两次访问间隔时间大于30秒将会重新开始计数。
大家也可以将这个脚本优化成,第一次封禁5分钟,第二次封禁半小时,第三次封禁半天,第四次封禁三天,第五次永久封禁等等。
好了,今天就到这儿吧,我是冰河,我们下期见~~
相关文章:

【实战】Nginx+Lua脚本+Redis 实现自动封禁访问频率过高IP
大家好,我是冰河~~ 自己搭建的网站刚上线,短信接口就被一直攻击,并且攻击者不停变换IP,导致阿里云短信平台上的短信被恶意刷取了几千条,加上最近工作比较忙,就直接在OpenResty上对短信接口做了一些限制&am…...

计算机专业大一课程:线性代数探秘
计算机专业大一课程:线性代数探秘 对于计算机专业的大一新生来说,线性代数是一门基础且重要的课程。它不仅是数学的一个分支,更是计算机科学中不可或缺的工具。那么,线性代数究竟包含哪些内容,对我们的计算机学习有何…...
vscode写markdown插入图片视频并放在指定目录
目录 前言正文 前言 各种云文档非常好用,但是当你想把这些资料保存在本地时,markdown我觉得是最好的选择 markdown编辑器也有很多,但我还是觉得vscode最好用,直接粘贴文件就可以插入也类似云文档的使用体验,但是想要…...
鸿蒙富文本显示
1.使用 RichText 组件(ArkTS) 背景知识:在 ArkTS(一种鸿蒙应用开发语言)中,RichText组件提供了更强大的富文本显示功能。它允许设置不同的文本样式,包括字体、颜色、字号等多种属性。 Rich Te…...

手写mybatis之细化XML语句构建器,完善静态SQL解析
前言 1:在流程上,通过 DefaultSqlSession#selectOne 方法调用执行器,并通过预处理语句处理器 PreparedStatementHandler 执行参数设置和结果查询。 2:那么这个流程中我们所处理的参数信息,也就是每个 SQL 执行时&#…...

使用Milvus和Llama-agents构建更强大的Agent系统
代理(Agent)系统能够帮助开发人员创建智能的自主系统,因此变得越来越流行。大语言模型(LLM)能够遵循各种指令,是管理 Agent 的理想选择,在许多场景中帮助我们尽可能减少人工干预、处理更多复杂任…...

Python 工具库每日推荐【Arrow】
文章目录 引言Python时间日期处理库的重要性今日推荐:Arrow工具库主要功能:使用场景:安装与配置快速上手示例代码代码解释实际应用案例案例:跨时区会议安排器案例分析高级特性时间范围和区间自定义时间格式扩展阅读与资源优缺点分析优点:缺点:总结【 已更新完 TypeScript…...

Win10 安装 Redis 数据库
一、Redis 数据库介绍 Redis 是一个开源的高性能键值对(key-value)的非关系型数据库。它通常用作数据结构服务器,支持多种类型的数据结构,如字符串(strings)、哈希(hashes)、列表&a…...

使用springboot生成war包
1.生成war包 1.1 更改pom包 打开一个springboot 项目 ,右击项目名从项目管理器打开 在pom.xml文件中插入以下两个依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><…...

见微知著:OpenEuler系统启动流程
OpenEuler是一个开源的Linux发行版,它的启动流程涉及到多个阶段,包括固件初始化、引导加载程序、内核启动、初始化系统和服务管理器等。下面将详细介绍OpenEuler的启动流程。 一、启动流程 1. 固件初始化(BIOS/UEFI) 启动过程首…...

支持向量机-笔记
支持向量机(Support Vector Machine, SVM) 是一种强大的监督学习算法,广泛应用于分类和回归任务,特别是在分类问题中表现优异。SVM 的核心思想是通过寻找一个最优超平面,将不同类别的数据点进行分割,并最大…...

研发线上事故风险解读之缓存篇
专业在线打字练习平台-巧手打字通,只输出有价值的知识。 一 前言 本文继续基于《线上事故案例集》,进一步深入梳理线上事故缓存使用方面的问题点,重点关注缓存在使用和优化过程中可能出现的问题,旨在为读者提供具有实践指导意义的…...
JavaScript前端开发技术
JavaScript前端开发技术 引言 JavaScript(简称JS)是一种广泛使用的脚本语言,特别在前端开发领域,它几乎成为了网页开发的标配。从简单的表单验证到复杂的单页应用(SPA),JavaScript都扮演着不可…...
H.264 编码参数优化策略
一、概述 随着数字媒体技术的发展,视频编码成为了多媒体领域中的重要研究方向之一。而H.264作为一种广泛应用的视频编码标准,具有高压缩比、优质画面和广泛兼容性等优点。为了进一步提高视频质量和压缩效率,对H.264编码参数进行优化成为了一个…...
C++ 游戏开发技术选型指南
C 游戏开发技术选型指南 游戏开发是一个复杂而多元化的领域,而C凭借其高性能和强大的控制能力,成为许多游戏引擎的首选编程语言。在这篇博客中,我们将探讨如何选择合适的C技术栈进行游戏开发,包括技术背景、代码示例、优化实践、…...

基于Python Django的在线考试管理系统
🍊作者:计算机毕设匠心工作室 🍊简介:毕业后就一直专业从事计算机软件程序开发,至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长:按照需求定制化开发项目…...

《Java基础》变量和数据类型
综述 在开始学习变量之前,我们思考一下为什么需要使用变量。 首先我们从小开始学习加法减法的时候,后来我们再学更难的东西就是代数,其中的x和y是我们要求解的内容,这些内容就是变量。 变量是人的思维的提升,没有变量…...

FLINK内存管理解析,taskmanager、jobmanager
1、在 Flink 中设置内存的方法是配置以下两个选项之一: 1)Total Flink memory:taskmanager.memory.flink.sizejobmanager.memory.flink.size 2)Total process memory:taskmanager.memory.process.sizejobmanager.mem…...

【AI论文精读13】RAG论文综述2(微软亚研院 2409)P5-可解释推理查询L3
AI知识点总结:【AI知识点】 AI论文精读、项目、思考:【AI修炼之路】 P1,P2,P3,P4 五、可解释推理查询(L3) ps:P2有四种查询(L1,L2,L3,…...

优达学城 Generative AI 课程3:Computer Vision and Generative AI
文章目录 1 官方课程内容自述第 1 课:图像生成简介第 2 课:计算机视觉基础第 3 课:图像生成与生成对抗网络(GANs)第 4 课:基于 Transformer 的计算机视觉模型第 5 课:扩散模型第 6 课࿰…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...

CVE-2023-25194源码分析与漏洞复现(Kafka JNDI注入)
漏洞概述 漏洞名称:Apache Kafka Connect JNDI注入导致的远程代码执行漏洞 CVE编号:CVE-2023-25194 CVSS评分:8.8 影响版本:Apache Kafka 2.3.0 - 3.3.2 修复版本:≥ 3.4.0 漏洞类型:反序列化导致的远程代…...

二叉树-144.二叉树的前序遍历-力扣(LeetCode)
一、题目解析 对于递归方法的前序遍历十分简单,但对于一位合格的程序猿而言,需要掌握将递归转化为非递归的能力,毕竟递归调用的时候会调用大量的栈帧,存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧,而非…...