【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 Nginx+Lua 实现基于 IP 的访问频率限制

在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制。

1.环境准备

在开始之前,请确保已安装以下组件:

• Nginx:作为反向代理服务器。

• Lua 模块:需要在 Nginx 中编译或安装ngx_http_lua_module

• Redis:作为数据存储,用于记录访问频率。

2.方案概述

我们将通过 Lua 脚本和 Redis 实现以下功能:

• 记录每个 IP 的访问频率。

• 设置阈值,当某个 IP 的访问次数超过此阈值时,将其加入黑名单。

• 每次请求时检查该 IP 是否在黑名单中。

3.Redis 数据结构

在 Redis 中,我们可以使用以下数据结构:

• 访问计数存储:ip:<client_ip>,值为该 IP 的请求次数。

• 黑名单存储:blocked_ip:<client_ip>,值为 1(表示被封禁)。

4.Lua 脚本示例

以下是一个完整的 Lua 脚本示例,用于实现访问频率限制和封禁逻辑:

lua 复制代码
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)  -- 1秒超时

-- 获取客户端 IP
local client_ip = ngx.var.remote_addr
local max_requests = 100  -- 设置最大请求次数
local expire_time = 60    -- 设置过期时间为60秒
local block_time = 300    -- 设置封禁时间为300秒

-- 连接 Redis
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
    ngx.say("failed to connect to Redis: ", err)
    return
end

-- 检查是否被封禁
local blocked_res, err = red:get("blocked_ip:" .. client_ip)
if blocked_res == "1" then
    ngx.exit(ngx.HTTP_FORBIDDEN)  -- 如果被封禁,返回403
end

-- 记录访问次数
local count, err = red:incr("ip:" .. client_ip)
if not count then
    ngx.say("failed to increment IP counter: ", err)
    return
end

-- 设置过期时间
if count == 1 then
    red:expire("ip:" .. client_ip, expire_time)
end

-- 检查访问次数是否超过阈值
if count > max_requests then
    red:set("blocked_ip:" .. client_ip, 1)
    red:expire("blocked_ip:" .. client_ip, block_time)  -- 封禁300秒
    ngx.exit(ngx.HTTP_FORBIDDEN)  -- 返回403
end

-- 关闭 Redis 连接
red:close()

5.Nginx 配置

接下来,我们需要在 Nginx 配置文件中使用此 Lua 脚本。

nginx 复制代码
http {
    lua_shared_dict limit_dict 10m;  # 定义共享内存字典

    server {
        listen 80;
        server_name example.com;

        location / {
            access_by_lua_block {
                -- 在此使用上述的 Lua 脚本
                local script = [[
                    local redis = require "resty.redis"
                    local red = redis:new()
                    red:set_timeout(1000)  -- 1秒超时

                    -- 获取客户端 IP
                    local client_ip = ngx.var.remote_addr
                    local max_requests = 100  -- 设置最大请求次数
                    local expire_time = 60    -- 设置过期时间为60秒
                    local block_time = 300    -- 设置封禁时间为300秒

                    -- 连接 Redis
                    local ok, err = red:connect("127.0.0.1", 6379)
                    if not ok then
                        ngx.say("failed to connect to Redis: ", err)
                        return
                    end

                    -- 检查是否被封禁
                    local blocked_res, err = red:get("blocked_ip:" .. client_ip)
                    if blocked_res == "1" then
                        ngx.exit(ngx.HTTP_FORBIDDEN)  -- 如果被封禁,返回403
                    end

                    -- 记录访问次数
                    local count, err = red:incr("ip:" .. client_ip)
                    if not count then
                        ngx.say("failed to increment IP counter: ", err)
                        return
                    end

                    -- 设置过期时间
                    if count == 1 then
                        red:expire("ip:" .. client_ip, expire_time)
                    end

                    -- 检查访问次数是否超过阈值
                    if count > max_requests then
                        red:set("blocked_ip:" .. client_ip, 1)
                        red:expire("blocked_ip:" .. client_ip, block_time)  -- 封禁300秒
                        ngx.exit(ngx.HTTP_FORBIDDEN)  -- 返回403
                    end

                    -- 关闭 Redis 连接
                    red:close()
                ]]
                ngx.execute_lua(script)
            }
            proxy_pass http://backend;
        }
    }
}

6.测试

• 启动 Redis:

bash 复制代码
   redis-server

• 启动 Nginx:

bash 复制代码
   nginx -s reload

• 访问测试:

• 使用浏览器或工具(如curl)访问你的 Nginx 服务。

• 在 60 秒内多次访问,观察是否触发封禁逻辑。

7.注意事项

• 连接池:

• 在生产环境中,建议使用连接池来管理 Redis 连接,避免频繁建立和关闭连接。

• 错误处理:

• 在 Lua 脚本中,确保对 Redis 连接失败等错误进行适当的处理,避免影响正常服务。

• 性能优化:

• 根据实际需求调整max_requestsexpire_timeblock_time等参数,以达到最佳性能。

总结

通过结合 Nginx、Lua 和 Redis,我们实现了一个简单而有效的基于 IP 的访问频率限制机制。这种方案不仅提高了服务器的安全性,还能有效防止恶意攻击。在实际应用中,你可以根据具体的场景和需求调整访问频率的限制和封禁策略。希望这些方法和示例对你有所帮助!

相关推荐
小鱼小鱼.oO1 小时前
阿里云服务器安装nginx并配置前端资源路径(前后端部署到一台服务器并成功访问)
服务器·nginx·阿里云
广东数字化转型4 小时前
nginx怎么使用nginx-rtmp-module模块实现直播间功能
linux·运维·nginx
浩浩测试一下5 小时前
Authpf(OpenBSD)认证防火墙到ssh连接到SSH端口转发技术栈 与渗透网络安全的关联 (RED Team Technique )
网络·网络协议·tcp/ip·安全·网络安全·php
孤寂大仙v6 小时前
【计算机网络】网络层IP协议与子网划分详解:从主机通信到网络设计的底层逻辑
tcp/ip·计算机网络·php
漫步者TZ8 小时前
【Netty系列】解决TCP粘包和拆包:LengthFieldBasedFrameDecoder
java·网络协议·tcp/ip·netty
hgdlip11 小时前
重启路由器ip不变怎么回事?原因分析与解决方法
网络协议·tcp/ip·智能路由器
hgdlip12 小时前
手机上网可以固定ip地址吗?详细解析
网络·tcp/ip·智能手机
2501_9159214312 小时前
高敏感应用如何保护自身不被逆向?iOS 安全加固策略与工具组合实战(含 Ipa Guard 等)
websocket·网络协议·tcp/ip·http·网络安全·https·udp
2501_9151063212 小时前
App 上线后还能加固吗?iOS 应用的动态安全补强方案实战分享(含 Ipa Guard 等工具组合)
websocket·网络协议·tcp/ip·http·网络安全·https·udp
wcjwdq13 小时前
Lua和JS的继承原理
javascript·lua·脚本继承