Posted in

网站刚线上就被攻击了,随后我一顿操作。。。 – 掘金_AI阅读总结 — 包阅AI

包阅导读总结

1. 关键词:网站攻击、短信接口、OpenResty、Lua、Redis

2. 总结:

– 作者自建网站上线后短信接口遭攻击,攻击者变换 IP 恶意刷取短信。

– 先采用临时方案,通过查看 Nginx 日志、过滤提取攻击 IP 并设置 Nginx 封禁,放入 crontab 任务。

– 后调整为 OpenResty+Lua 结合 Redis 的方案,实现自动封禁和解封访问频率过高的 IP。

3. 主要内容:

– 网站刚上线短信接口就被攻击

– 攻击者不停变换 IP,恶意刷取阿里云短信平台几千条短信。

– 临时解决方案

– 查看 Nginx 日志发现被攻击的 IP 和接口。

– 用脚本过滤日志提取攻击短信接口的 IP 并存入 txt 文件,重启 Nginx。

– 设置 Nginx 读取过滤出的 IP 进行封禁。

– 将过滤脚本放进 crontab 任务每分钟执行一次。

– OpenResty+Lua 方案

– 安装 OpenResty

– 安装 Redis

– Lua 访问 Redis

– OpenResty+Lua 实现

– 添加访问控制的 Lua 脚本。

– 在需要访问限制的 location 里添加代码。

– 测试访问,查看 Redis 中 IP 的访问计数和封禁情况。

思维导图:

文章地址:https://juejin.cn/post/7399109720457543721

文章来源:juejin.cn

作者:冰_河

发布时间:2024/8/4 3:47

语言:中文

总字数:3397字

预计阅读时间:14分钟

评分:84分

标签:网络安全,应急响应,安全策略,OpenResty,Lua


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

大家好,我是冰河~~

自己搭建的网站刚上线,短信接口就被一直攻击,并且攻击者不停变换IP,导致阿里云短信平台上的短信被恶意刷取了几千条,加上最近工作比较忙,就直接在OpenResty上对短信接口做了一些限制,采用OpenResty+Lua的方案成功动态封禁了频繁刷短信接口的IP。

一、临时解决方案

由于事情比较紧急,所以,当发现这个问题时,就先采用快速的临时方案解决。

(1)查看Nginx日志发现被攻击的IP 和接口

[root@binghe ~]

发现攻击者一直在用POST请求 /fhtowers/user/getVerificationCode这个接口

2024-07-28-001.png

(2)用awk和grep脚本过滤nginx日志,提取攻击短信接口的ip(一般这个接口是用来发注册验证码的,一分钟如果大于10次请求的话就不是正常的访问请求了,大家根据自己的实际情况更改脚本)并放到一个txt文件中去,然后重启nginx

[root@binghe ~]nginx_home=/usr/local/openresty/nginxlog_path=/var/log/nginx/access.logtail -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() do		  if ngx.var.http_x_forwarded_for == line then   #如果ip在黑名单列表里直接返回403				 ngx.exit(ngx.HTTP_FORBIDDEN)		  end  end '; proxy_pass http://appservers;   }

(4)把过滤脚本放进crontab任务里,一分钟执行一次

[root@binghe ~]*/1 * * * * sh /root/denyip.sh

(5)查看一下效果,发现攻击者的请求都被返回403并拒绝了

2024-07-28-002.png

二、OpenResty+Lua方案

临时方案有效果后,再将其调整成使用OpenResty+Lua脚本的方案,来一张草图。

2024-07-28-003.png

接下来,就是基于OpenResty和Redis实现自动封禁访问频率过高的IP。

2.1 安装OpenResty

安装使用 OpenResty,这是一个集成了各种 Lua 模块的 Nginx 服务器,是一个以Nginx为核心同时包含很多第三方模块的Web应用服务器,使用Nginx的同时又能使用lua等模块实现复杂的控制。

(1)安装编译工具、依赖库

[root@test1 ~]

(2)下载openresty-1.13.6.1.tar.gz 源码包,并解压;下载ngx_cache_purge模块,该模块用于清理nginx缓存;下载nginx_upstream_check_module模块,该模块用于ustream健康检查。

[root@test1 ~][root@test1 local][root@test1 local][root@test1 local][root@test1 local][root@test1 local][root@test1 local][root@test1 local]

(3)配置需安装的模块

[root@test1 openresty-1.13.6.1][root@test1 openresty-1.13.6.1]

(4)创建一个软链接方便启动停止

[root@test1 ~]

(5)启动nginx

[root@test1 ~][root@test1 ~]

如果启动时候报错找不到PID的话就用以下命令解决(如果没有更改过目录的话,让它去读nginx的配置文件就好了)

[root@test1 ~]

2024-07-28-004.png

随后,打开浏览器访问页面。

2024-07-28-005.png

(6)在Nginx上测试一下能否使用Lua脚本

[root@test1 ~]

在server里面加一个

location /lua {	default_type text/plain;	content_by_lua ‘ngx.say(“hello,lua!”)’;}

2024-07-28-006.png

加完后重新reload配置。

[root@test1 ~]

在浏览器里输入 ip地址/lua,出现下面的字就表示Nginx能够成功使用lua了

2024-07-28-007.png

2.2 安装Redis

(1)下载、解压、编译安装

[root@test1 ~][root@test1 local][root@test1 local][root@test1 local][root@test1 redis-6.0.1][root@test1 redis-6.0.1]

(2)查看是否安装成功

[root@test1 redis-6.0.1][root@test1 redis-6.0.1]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][root@test1 etc][root@test1 etc][root@test1 var][root@test1 var][root@test1 redis]

(4)修改配置文件

[root@test1 redis][root@test1 redis-6.0.1][root@test1 redis-6.0.1]bind 192.168.1.222port 6379pidfile /var/redis/run/redis_6379.pidlogfile /var/redis/log/redis.logdir /var/redis/datadaemonize yes

(5)设置启动方式

[root@test1 redis-6.0.1][root@test1 utils][root@test1 utils]

/etc/init.d/redis文件的内容如下。

#!/bin/shREDISPORT=6379EXEC=/usr/local/bin/redis-serverCLIEXEC=/usr/local/bin/redis-cliPIDFILE=/var/run/redis_${REDISPORT}.pidCONF="/etc/redis/${REDISPORT}.conf"case "$1" in    start)        if [ -f $PIDFILE ]        then                echo "$PIDFILE exists, process is already running or crashed"        else                echo "Starting Redis server..."                $EXEC $CONF        fi        ;;    stop)        if [ ! -f $PIDFILE ]        then                echo "$PIDFILE does not exist, process is not running"        else                PID=$(cat $PIDFILE)                echo "Stopping ..."                $CLIEXEC -p $REDISPORT shutdown                while [ -x /proc/${PID} ]                do                    echo "Waiting for Redis to shutdown ..."                    sleep 1                done                echo "Redis stopped"        fi        ;;    *)        echo "Please use start or stop as first argument"        ;;esac

增加执行权限,并启动Redis。

[root@test1 utils][root@test1 utils]

(6)查看redis是否启动

2024-07-28-008.png

2.3 Lua访问Redis

(1)连接redis,然后添加一些测试参数

[root@test1 utils]192.168.1.222:6379> set "123" "456"OK

(2)编写连接Redis的Lua脚本

[root@test1 utils]local redis = require "resty.redis"local conn = redis.new()conn.connect(conn, '192.168.1.222', '6379')     local res = conn:get("123")if res==ngx.null then    ngx.say("redis集群中不存在KEY——'123'")    returnendngx.say(res)

(3)在nginx.conf配置文件中的server下添加以下location

[root@test1 utils]location /lua_redis {	default_type text/plain;	content_by_lua_file /usr/local/openresty/nginx/conf/lua/redis.lua;}

随后重新reload配置。

[root@test1 utils]

(4)验证Lua访问Redis的正确性

在浏览器输入ip/lua_redis, 如果能看到下图的内容表示Lua可以访问Redis。

2024-07-28-009.png

准备工作已经完成,现在要实现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]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中定义的业务标识符,也可以不加,不过加了后方便区分--连接redislocal 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 then    goto FLAGend--查询ip是否被禁止访问,如果存在则返回403错误代码is_block, err = conn:get(BUSINESS.."-BLOCK-"..ngx.var.remote_addr)  if is_block == '1' then    ngx.exit(403)    goto FLAGend--查询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_out    res, err = conn:set(BUSINESS.."-COUNT-"..ngx.var.remote_addr, 1)	res, err = conn:expire(BUSINESS.."-COUNT-"..ngx.var.remote_addr, ip_time_out)else    ip_count = ip_count + 1 --存在则将单位时间内的访问次数加1      if ip_count >= ip_max_count then --如果超过单位时间限制的访问次数,则添加限制访问标识,限制时间为ip_block_time        res, err = conn:set(BUSINESS.."-BLOCK-"..ngx.var.remote_addr, 1)        res, err = conn:expire(BUSINESS.."-BLOCK-"..ngx.var.remote_addr, ip_block_time)	else        res, err = conn:set(BUSINESS.."-COUNT-"..ngx.var.remote_addr,ip_count)		res, err = conn:expire(BUSINESS.."-COUNT-"..ngx.var.remote_addr, ip_time_out)    endend-- 结束标记::FLAG::local ok, err = conn:close()

(2)在需要做访问限制的location里加两段代码即可,这里用刚才的/lua做演示

[root@test1 lua]

2024-07-28-010.png

主要是添加如下配置。

access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;

其中,set $business “lua” 是为了把IP放进Redis的时候标明是哪个location的,可以不加这个配置。

随后,重新reload配置。

[root@test1 lua]

(3)打开浏览器访问192.168.1.222/lua 并一直按F5刷新。

2024-07-28-011.png

随后,连接Redis,查看IP的访问计数。

[root@test1 ~]

发现redis已经在统计访问lua这个网页ip的访问次数了

2024-07-28-012.png

这个key的过期时间是30秒,如果30秒没有重复访问20次这个key就会消失,所以说正常用户一般不会触发这个封禁的脚本。

2024-07-28-013.png

当30秒内访问超过了20次,发现触发脚本了,变成了403

2024-07-28-014.png

再次查看Redis的key,发现多了一个lua-block-192.168.1.158,过期时间是300秒,就是说在300秒内这个ip无法继续访问192.168.1.222/lua这个页面了。

2024-07-28-015.png

过五分钟后再去访问这个页面,又可以访问了。

2024-07-28-016.png

这个脚本的目的很简单:一个IP如果在30秒内其访问次数达到20次则表明该IP访问频率太快了,因此将该IP封禁5分钟。同时由于计数的KEY在Redis中的超时时间设置成了30秒,所以如果两次访问间隔时间大于30秒将会重新开始计数。

大家也可以将这个脚本优化成,第一次封禁5分钟,第二次封禁半小时,第三次封禁半天,第四次封禁三天,第五次永久封禁等等。

好了,今天就到这儿吧,我是冰河,我们下期见~~