Nginx 实现动态封禁IP,详细教程来了

科技   2024-11-14 11:26   山西  

聊一个我最近处理的技术难题——如何用 Nginx 实现动态封禁 IP,防止一些讨厌的爬虫或者恶意用户对你的网站进行恶意攻击。

想必大家也遇到过那些爬虫一遍又一遍疯狂请求服务器,吃掉了大量的带宽资源,或者有些恶意用户尝试用暴力破解方式登陆,着实让人头疼。

幸好,Nginx 和 Lua 结合 Redis,给我们提供了一个既高效又灵活的方案。

1. 背景与需求

有时候,你的服务器会遭遇一些恶意的攻击,常见的就是爬虫、暴力破解、SQL 注入,甚至 DDoS 攻击。

这些行为不仅增加了服务器负担,还可能导致资源浪费,甚至直接造成服务中断。解决这些问题的一种方式就是通过 IP 封禁,尤其是对于那些恶意访问频繁的 IP 地址。封禁的策略应该是动态的,可以随时添加和移除 IP,同时还要控制封禁的时长,以避免误伤正常用户。

我们希望通过以下几个步骤来实现这个目标:

  1. 封禁爬虫与恶意用户请求:识别并封禁那些频繁请求且行为恶意的 IP 地址。
  2. 建立动态 IP 黑名单:这个黑名单需要能动态更新,支持实时封禁。
  3. 封禁失效时间设置:封禁的 IP 不可能永远存在黑名单中,我们需要设置一个失效时间,自动解封。

2. 方案设计

解决这个问题的方案有好几种,每种方法都有其优缺点。

我们可以从操作系统、Web 服务器或应用层面入手,选择适合的技术栈。下面我们分析一下每种方案。

方法 1:操作系统层面的拦截(iptables)

iptables 是 Linux 系统自带的防火墙工具,通过它可以轻松封禁 IP。不过,这种方式的缺点是操作繁琐且不灵活。每当需要动态封禁某个 IP 时,我们得手动去操作,不太适合自动化管理。而且,这样的封禁是全局生效的,一旦添加了黑名单中的 IP,它就完全无法访问服务器,除非手动移除。

方法 2:Web 服务器层面的拦截(Nginx + Lua)

这种方法是我们今天的重点,利用 Nginx 的 Lua 模块配合 Redis,可以非常灵活地动态封禁 IP。Nginx 负责处理请求,Lua 脚本在请求到来时判断 IP 是否需要封禁,而 Redis 则用来存储 IP 黑名单和封禁时长。

这个方法的优点是:

  • 动态封禁:我们可以设置一个超时机制,自动解封被封禁的 IP。
  • 分布式管理:通过 Redis 存储黑名单,可以在多台服务器间共享数据,方便管理。
  • 灵活性:可以对封禁策略进行动态调整,甚至支持高级的 IP 限制和行为监控。

缺点是:我们需要了解 Nginx 配置、Lua 脚本编写以及 Redis 的使用。

方法 3:应用层面的拦截(代码实现)

这种方法通过在应用层直接判断 IP 是否在黑名单中来控制访问。实现起来简单直观,但性能不如前两者。每当一个请求到来时,代码会去查询黑名单,这对于高并发场景可能会带来一定的性能压力。

3. Nginx + Lua + Redis 方案实现

接下来,我们深入讲解如何通过 Nginx 配合 Lua 脚本以及 Redis 实现动态 IP 封禁。首先,我们需要确保系统已经安装了 Lua 和 Redis,并且 Nginx 已经配置了 Lua 模块。

Nginx 配置

首先,在 Nginx 配置文件中,我们需要启用 Lua 模块,并且指定 Lua 脚本来处理访问控制。修改 nginx.conf 配置文件,在需要处理请求的 location 块中添加 Lua 脚本调用。

http {
lua_shared_dict limit_req_zone 10m; # 用来存放 Redis 连接池
server {
listen 80;
location / {
set $limit 0;
access_by_lua_file /etc/nginx/lua/access_limit.lua; # 引用 Lua 脚本
}
}
}

Lua 脚本实现(access_limit.lua)

接下来我们实现 access_limit.lua 脚本,主要完成以下几个任务:

  1. 连接 Redis。
  2. 获取请求中的客户端 IP 地址。
  3. 判断该 IP 是否在黑名单中。
  4. 如果不在黑名单中,则记录访问次数;如果在黑名单中,则拒绝请求。
local redis = require "resty.redis"  -- 引入 Redis 库
local red = redis:new()  -- 创建 Redis 对象
red:set_timeout(1000)  -- 设置连接超时时间为 1 秒

-- 连接到 Redis 服务器
local ok, err = red:connect("127.0.0.1"6379)
if not ok then
    ngx.log(ngx.ERR, "failed to connect to Redis: ", err)
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

-- 获取客户端 IP 地址
local client_ip = ngx.var.remote_addr

-- 检查该 IP 是否在黑名单中
local res, err = red:get("blacklist:" .. client_ip)
if res == "1" then
    ngx.log(ngx.ERR, "IP " .. client_ip .. " is blacklisted")
    return ngx.exit(ngx.HTTP_FORBIDDEN)
end

-- 如果 IP 不在黑名单中,则统计访问次数
local visits, err = red:get("visits:" .. client_ip)
if visits == ngx.null then
    visits = 0
end
visits = visits + 1

-- 如果访问次数超过设定阈值,则加入黑名单
if visits > 10 then  -- 设定的阈值为 10 次请求
    red:set("blacklist:" .. client_ip, 1)
    red:expire("blacklist:" .. client_ip, 3600)  -- 设置封禁时长为 1 小时
end

-- 更新访问次数
red:set("visits:" .. client_ip, visits)
red:expire("visits:" .. client_ip, 60)  -- 设置过期时间为 1 分钟

这个脚本通过 Redis 存储访问数据并动态判断是否需要封禁 IP。如果某个 IP 在短时间内超过了访问次数限制(比如 10 次),它就会被封禁 1 小时。

Redis 配置与连接

在这段 Lua 脚本中,我们通过 resty.redis 模块连接 Redis 服务器。Redis 在这里充当了一个缓存的角色,用来存储每个 IP 的访问次数和黑名单信息。使用 Redis 的好处是它具有高性能、易于扩展且支持分布式管理。

4. 方案总结

通过 Nginx + Lua + Redis,我们可以高效地实现 IP 封禁策略。这个方案的优点在于:

  • 配置简单,且无需改变业务代码。
  • 适合高并发的场景,对性能影响较小。
  • 支持动态配置,封禁时长可以灵活调整。
  • Redis 支持分布式管理,可以扩展到多台服务器。

此外,Redis 还可以用于实现更多高级功能,例如 IP 访问频率限制、暴力破解防护等。

5. 扩展与高级功能

除了基本的封禁功能,Nginx + Lua + Redis 还可以扩展为更强大的安全防护系统。例如:

  • 异常检测与自动封禁:通过分析访问日志,检测异常访问模式,自动封禁异常 IP。
  • 白名单机制:对于一些可信的 IP,可以设置白名单,让它们绕过封禁规则。
  • 验证码验证:对频繁访问的 IP 提供验证码验证,进一步阻止恶意访问。
  • 数据统计与分析:记录封禁的数据,通过分析日志来优化封禁策略。

总之,Nginx 与 Lua 脚本的结合,使得动态封禁 IP 成为一个简单而灵活的解决方案,不仅能有效防止恶意访问,还可以根据需要进行扩展与优化。

对编程、职场感兴趣的同学,可以链接我,微信:coder301 拉你进入“程序员交流群”。
🔥东哥私藏精品 热门推荐🔥

东哥作为一名超级老码农,整理了全网最全《Java高级架构师资料合集》

资料包含了《IDEA视频教程》《最全Java面试题库》、最全项目实战源码及视频》及《毕业设计系统源码》总量高达 650GB 。全部免费领取!全面满足各个阶段程序员的学习需求。

Java面试那些事儿
回复 java ,领取Java面试题。分享AI编程,Java教程,Java面试辅导,Java编程视频,Java下载,Java技术栈,AI工具,Java开源项目,Java简历模板,Java招聘,Java实战,Java面试经验,IDEA教程。
 最新文章