From 9085a708070a2fdc381c8d117ea1a24df77512dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E5=8A=9B=E4=B8=B8666?= <147456216+DaLiWan666@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:23:46 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E7=AC=AC=E4=B8=89=E6=96=B9=E8=A7=84?= =?UTF-8?q?=E5=88=99/=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第三方实现的插件与规则 --- src/plugins/third_party/auth-plugin.lua | 165 ++++++++++++++++++ .../brute-force-login-prevention.lua | 39 +++++ .../third_party/frequent-block-detection.lua | 28 +++ .../high-frequency-error-protection.lua | 53 ++++++ src/rules/third_party/maintenance-mode.lua | 94 ++++++++++ 5 files changed, 379 insertions(+) create mode 100644 src/plugins/third_party/auth-plugin.lua create mode 100644 src/rules/third_party/brute-force-login-prevention.lua create mode 100644 src/rules/third_party/frequent-block-detection.lua create mode 100644 src/rules/third_party/high-frequency-error-protection.lua create mode 100644 src/rules/third_party/maintenance-mode.lua diff --git a/src/plugins/third_party/auth-plugin.lua b/src/plugins/third_party/auth-plugin.lua new file mode 100644 index 0000000..dd63902 --- /dev/null +++ b/src/plugins/third_party/auth-plugin.lua @@ -0,0 +1,165 @@ +--- +--- +--- +--- 修订日期: 2024/12/20 +--- + +local ngx = ngx +local ngx_log = ngx.log +local ngx_ERR = ngx.ERR +local ngx_print = ngx.print +local ngx_exit = ngx.exit +local ngx_today = ngx.today +local ngx_kv = ngx.shared + +local _M = { + version = 1.7, + name = "auth-plugin" -- 插件名称 +} + +-- 配置 +local valid_domains = { + "test.com", -- 需要保护的域名列表 + "test1.cn" +} + +local valid_username = "admin" +local valid_password = "password123" -- 强密码建议修改 +local session_duration = 7200 -- 2小时,以秒为单位 +local max_login_attempts = 5 -- 最大登录失败次数 + +-- 处理特殊字符函数,防止 HTML 注入 +local function escape_html(str) + if not str then return "" end + local replacements = { + ["&"] = "&", + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["'"] = "'", + } + return (str:gsub("[&<>'\"]", function(c) return replacements[c] end)) +end + +-- 登录页面HTML模板,带错误提示信息 +local function get_login_page(req_uri, error_message) + local escaped_error_message = escape_html(error_message or "") + local form_action = escape_html(req_uri or "/") + + -- HTML部分拼接 + return [[ + + + + + + 身份验证 + + + +
+

身份验证

+ ]] .. (escaped_error_message ~= "" and '

' .. escaped_error_message .. '

' or "") .. [[ +
+ + + + + +
+

您的访问受保护,请输入正确的账号密码。

+
+ + + ]] +end + +-- 校验登录请求 +local function validate_login(waf) + local form = waf.form["FORM"] + if form then + local username = form["username"] + local password = form["password"] + if username == valid_username and password == valid_password then + return true + end + end + return false +end + +-- 请求阶段后过滤器 +function _M.req_post_filter(waf) + local host = waf.host + local req_uri = waf.reqUri + local method = waf.method + + -- 检查域名是否受保护 + local is_protected = false + for _, domain in ipairs(valid_domains) do + if string.lower(host) == string.lower(domain) then + is_protected = true + break + end + end + + if not is_protected then + return + end + + -- 检查登录失败的次数 + local login_attempts_key = "login_attempts:" .. waf.ip .. ":" .. host + local login_attempts = ngx_kv.ipCache and ngx_kv.ipCache:get(login_attempts_key) or 0 + + if login_attempts >= max_login_attempts then + -- 直接拦截超出登录尝试次数的请求 + ngx_kv.ipBlock:incr(waf.ip, 1, 0) -- 将IP拉入拦截列表 + waf.msg = "IP因登录失败次数过多已被拦截" + waf.rule_id = 10001 + waf.deny = true + return ngx_exit(403) -- 返回403 Forbidden + end + + -- 校验会话是否已认证 + local session_key = "auth:" .. waf.ip .. ":" .. host -- 结合 IP 和 域名生成唯一会话 + local is_authenticated = ngx_kv.ipCache and ngx_kv.ipCache:get(session_key) + + if not is_authenticated then + if method == "POST" then + if validate_login(waf) then + -- 登录成功:记录验证认证 + ngx_kv.ipCache:set(session_key, true, session_duration) + ngx_kv.ipCache:delete(login_attempts_key) -- 重置失败次数 + return + else + -- 登录失败:记录失败次数 + login_attempts = login_attempts + 1 + ngx_kv.ipCache:set(login_attempts_key, login_attempts, 3600) -- 失败次数保存1小时 + + -- 可选:输出登录失败提示(直接返回页面避免请求到原站) + local error_message = "用户名或密码错误,请重试。" + ngx.header.content_type = "text/html; charset=utf-8" + return ngx.print(get_login_page(req_uri, error_message)) -- 使用直接返回页面 + end + else + -- 显示登录页面 + ngx.header.content_type = "text/html; charset=utf-8" + return ngx.print(get_login_page(req_uri, nil)) -- 使用直接返回页面 + end + end + + -- 已经认证,继续处理后续请求 +end + +return _M diff --git a/src/rules/third_party/brute-force-login-prevention.lua b/src/rules/third_party/brute-force-login-prevention.lua new file mode 100644 index 0000000..a220532 --- /dev/null +++ b/src/rules/third_party/brute-force-login-prevention.lua @@ -0,0 +1,39 @@ +--[[ +规则名称: 防止爆破登录 +过滤阶段: 请求阶段 +危险等级: 高危 +规则描述: 针对路径中包含登录、注册等关键词的URL特征,如果5分钟(300秒)内请求次数超过10次,则封禁该IP 1440分钟(24小时) +--]] + +local sh = waf.ipCache +local bruteForceKey = 'brute-force-login:' .. waf.ip -- 使用独立前缀标识,避免与其他规则冲突 + +-- 定义特征路径关键词列表 +local targetPaths = { "login", "signin", "signup", "register", "reset", "passwd", "account", "user" } + +-- 判断URI是否包含特征关键词 +if not waf.pmMatch(waf.toLower(waf.uri), targetPaths) then + return false -- 如果路径中不包含任何特征关键词,则跳过检测 +end + +-- 获取缓存中的数据 +local requestCount, flag = sh:get(bruteForceKey) +if not requestCount then + -- 初始化计数,设置5分钟(300秒)的时间窗口 + sh:set(bruteForceKey, 1, 300, 1) +else + -- 如果标志已经为2,则IP处于封禁状态,直接拦截 + if flag == 2 then + return waf.block(true) -- 阻断请求,返回403响应 + end + + -- 增加非法请求次数 + sh:incr(bruteForceKey, 1) + if requestCount + 1 > 10 then + -- 达到爆破攻击检测阈值,标记为封禁状态,封禁时间为1440分钟(24小时) + sh:set(bruteForceKey, requestCount + 1, 86400, 2) + return true, "检测到登录接口发生爆破攻击,已封禁IP", true -- 日志载荷改为中文 + end +end + +return false diff --git a/src/rules/third_party/frequent-block-detection.lua b/src/rules/third_party/frequent-block-detection.lua new file mode 100644 index 0000000..7aab014 --- /dev/null +++ b/src/rules/third_party/frequent-block-detection.lua @@ -0,0 +1,28 @@ +--[[ +规则名称: 频繁触发攻击拦截的IP拉黑 +过滤阶段: 请求阶段 +危险等级: 高危 +规则描述: 检查当前请求的客户端IP是否在最近10分钟内频繁触发(超过30次)WAF的拦截,如果是,则拉黑IP 1440分钟,并记录日志。 +注意: 因为南墙WAF特性,此规则生效对规则ID有要求,需要将此规则与南墙自带规则的第一个规则交换位置才能生效。 +]] + +local sh = waf.ipCache -- 键值存储库,用于存储拉黑状态 +local ip_stats = waf.ipBlock -- 查询最近被南墙拦截的IP统计,如社区版本默认存储时间为10分钟 +local ip = waf.ip +local block_key = "blocked-" .. ip -- 用于记录IP拉黑状态的key + +-- 如果IP已经被拉黑则直接拦截 +local c, f = sh:get(block_key) +if c and f == 2 then + return waf.block(true) -- 重置TCP连接,不返回任何内容 +end + +-- 检查该IP在最近时间内是否频繁被拦截 +local recent_count = ip_stats:get(ip) +if recent_count and recent_count > 30 then + -- 如果超过30次,则拉黑IP,设置1440分钟(24小时) + sh:set(block_key, 1, 86400, 2) -- 第三个参数86400为1440分钟(单位为秒),第四个参数2表示拉黑状态 + return true, "IP频繁触发拦截,已被拉黑", true -- 记录日志并拦截 +end + +return false diff --git a/src/rules/third_party/high-frequency-error-protection.lua b/src/rules/third_party/high-frequency-error-protection.lua new file mode 100644 index 0000000..3cd2a37 --- /dev/null +++ b/src/rules/third_party/high-frequency-error-protection.lua @@ -0,0 +1,53 @@ +--[[ +规则名称: 高频错误防护 +过滤阶段: 返回HTTP头阶段 +危险等级: 中危 +规则描述: 监测频繁返回40x、50x错误,当60秒内出现这些错误10次以上,则封禁1440分钟。 +--]] + +local function isCommonError(status) + -- 检查是否为40x或50x错误 + return status >= 400 and status < 600 +end + +-- 配置参数 +local threshold = 10 -- 错误次数阈值 +local timeWindow = 60 -- 时间窗口,单位为秒 +local banDuration = 1440 * 60 -- 封禁时间,1440分钟 = 86400秒 + +-- 获取客户端IP +local ip = waf.ip + +-- 获取返回的HTTP状态码 +local status = waf.status + +-- 检查当前请求是否是40x或者50x错误,不是则直接返回false +if not isCommonError(status) then + return false +end + +-- 使用 waf.ipCache 记录当前 IP 的错误次数 +local errorCache = waf.ipCache +local errorKey = "error:" .. ip -- 定义记录错误次数的键值,以 IP 为基础区分 + +local errorCount, flag = errorCache:get(errorKey) + +-- 若当前记录不存在,初始化记录 +if not errorCount then + errorCache:set(errorKey, 1, timeWindow) -- 初始错误计数设置为1,并设置为60秒过期 +else + if flag == 2 then + -- 标志为2表示该IP已被封禁,直接拦截,即刻终止 + return waf.block(true) + end + + -- 累加错误计数 + errorCache:incr(errorKey, 1) + if errorCount + 1 >= threshold then + -- 达到错误频率阈值,标记当前IP为封禁状态 + errorCache:set(errorKey, errorCount + 1, banDuration, 2) + return true, "高频错误触发,IP已被封禁", true + end +end + +return false diff --git a/src/rules/third_party/maintenance-mode.lua b/src/rules/third_party/maintenance-mode.lua new file mode 100644 index 0000000..db14cea --- /dev/null +++ b/src/rules/third_party/maintenance-mode.lua @@ -0,0 +1,94 @@ +--[[ +规则名称: 站点维护模式 +过滤阶段: 请求阶段 +危险等级: 低危 +规则描述: 将站点置为维护模式,返回“网页正在维护”的自定义页面。 +--]] + +-- 检查是否启用维护模式的条件(可以根据需求自定义,以下为示例) +local maintenance_mode = true -- 可以通过配置文件或其他方式动态控制 + +if maintenance_mode then + -- 设置自定义的维护页面 HTML 内容 + local maintenance_html = [[ + + + + + 网页正在维护 + + + +
+
+ + + + +
+

网页正在维护

+

抱歉,当前网页正在维护中,请稍后访问。

+
维护通知由 南墙 WAF 提供
+
+ + ]] + + ngx.header.content_type = "text/html; charset=utf-8" + + -- 输出维护页面并终止请求处理 + ngx.print(maintenance_html) + return ngx.exit(ngx.HTTP_OK) -- 使用 ngx.HTTP_OK 结束请求,避免传递到源站 +end + +return false -- 未启用维护模式,不拦截请求 From 06b4c2b699a232d354916d44b26d1b3886f58f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E5=8A=9B=E4=B8=B8666?= <147456216+DaLiWan666@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:31:55 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=BA=9B?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../third_party/brute-force-login-prevention.lua | 2 +- .../third_party/high-frequency-error-protection.lua | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/rules/third_party/brute-force-login-prevention.lua b/src/rules/third_party/brute-force-login-prevention.lua index a220532..011c5a7 100644 --- a/src/rules/third_party/brute-force-login-prevention.lua +++ b/src/rules/third_party/brute-force-login-prevention.lua @@ -24,7 +24,7 @@ if not requestCount then else -- 如果标志已经为2,则IP处于封禁状态,直接拦截 if flag == 2 then - return waf.block(true) -- 阻断请求,返回403响应 + return waf.block(true) -- 阻断请求 end -- 增加非法请求次数 diff --git a/src/rules/third_party/high-frequency-error-protection.lua b/src/rules/third_party/high-frequency-error-protection.lua index 3cd2a37..410187a 100644 --- a/src/rules/third_party/high-frequency-error-protection.lua +++ b/src/rules/third_party/high-frequency-error-protection.lua @@ -2,12 +2,13 @@ 规则名称: 高频错误防护 过滤阶段: 返回HTTP头阶段 危险等级: 中危 -规则描述: 监测频繁返回40x、50x错误,当60秒内出现这些错误10次以上,则封禁1440分钟。 +规则描述: 监测频繁返回400、401、403、404、405、429、444错误,当60秒内出现这些错误10次以上,则封禁1440分钟。 --]] -local function isCommonError(status) - -- 检查是否为40x或50x错误 - return status >= 400 and status < 600 +local function isSpecifiedError(status) + -- 检查是否为指定的状态码,限定在 [400, 401, 403, 404, 405, 429, 444] + local allowed_errors = {400, 401, 403, 404, 405, 429, 444} + return waf.inArray(status, allowed_errors) end -- 配置参数 @@ -21,8 +22,8 @@ local ip = waf.ip -- 获取返回的HTTP状态码 local status = waf.status --- 检查当前请求是否是40x或者50x错误,不是则直接返回false -if not isCommonError(status) then +-- 检查当前请求是否是指定的状态码错误,不是则直接返回false +if not isSpecifiedError(status) then return false end From 202a234c11682ef4844b1c42aaf1074016260bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E5=8A=9B=E4=B8=B8666?= <147456216+DaLiWan666@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:01:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E4=BA=86=E4=BA=9B?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/third_party/auth-plugin.lua | 35 ++----- .../brute-force-login-prevention.lua | 31 +++--- .../third_party/frequent-block-detection.lua | 31 +++--- .../high-frequency-error-protection.lua | 17 +--- src/rules/third_party/maintenance-mode.lua | 94 ------------------- 5 files changed, 48 insertions(+), 160 deletions(-) delete mode 100644 src/rules/third_party/maintenance-mode.lua diff --git a/src/plugins/third_party/auth-plugin.lua b/src/plugins/third_party/auth-plugin.lua index dd63902..3fd305c 100644 --- a/src/plugins/third_party/auth-plugin.lua +++ b/src/plugins/third_party/auth-plugin.lua @@ -1,7 +1,6 @@ --- ---- ---- ---- 修订日期: 2024/12/20 +--- 作者: MCQSJ(https://github.com/MCQSJ) +--- 更新日期: 2024/12/21 --- local ngx = ngx @@ -13,7 +12,7 @@ local ngx_today = ngx.today local ngx_kv = ngx.shared local _M = { - version = 1.7, + version = 0.1, name = "auth-plugin" -- 插件名称 } @@ -28,7 +27,6 @@ local valid_password = "password123" -- 强密码建议修改 local session_duration = 7200 -- 2小时,以秒为单位 local max_login_attempts = 5 -- 最大登录失败次数 --- 处理特殊字符函数,防止 HTML 注入 local function escape_html(str) if not str then return "" end local replacements = { @@ -41,12 +39,10 @@ local function escape_html(str) return (str:gsub("[&<>'\"]", function(c) return replacements[c] end)) end --- 登录页面HTML模板,带错误提示信息 local function get_login_page(req_uri, error_message) local escaped_error_message = escape_html(error_message or "") local form_action = escape_html(req_uri or "/") - -- HTML部分拼接 return [[ @@ -86,7 +82,6 @@ local function get_login_page(req_uri, error_message) ]] end --- 校验登录请求 local function validate_login(waf) local form = waf.form["FORM"] if form then @@ -99,13 +94,11 @@ local function validate_login(waf) return false end --- 请求阶段后过滤器 function _M.req_post_filter(waf) local host = waf.host local req_uri = waf.reqUri local method = waf.method - -- 检查域名是否受保护 local is_protected = false for _, domain in ipairs(valid_domains) do if string.lower(host) == string.lower(domain) then @@ -118,48 +111,40 @@ function _M.req_post_filter(waf) return end - -- 检查登录失败的次数 local login_attempts_key = "login_attempts:" .. waf.ip .. ":" .. host local login_attempts = ngx_kv.ipCache and ngx_kv.ipCache:get(login_attempts_key) or 0 if login_attempts >= max_login_attempts then - -- 直接拦截超出登录尝试次数的请求 - ngx_kv.ipBlock:incr(waf.ip, 1, 0) -- 将IP拉入拦截列表 + ngx_kv.ipBlock:incr(waf.ip, 1, 0) waf.msg = "IP因登录失败次数过多已被拦截" waf.rule_id = 10001 waf.deny = true - return ngx_exit(403) -- 返回403 Forbidden + return ngx_exit(403) end - -- 校验会话是否已认证 - local session_key = "auth:" .. waf.ip .. ":" .. host -- 结合 IP 和 域名生成唯一会话 + local session_key = "auth:" .. waf.ip .. ":" .. host local is_authenticated = ngx_kv.ipCache and ngx_kv.ipCache:get(session_key) if not is_authenticated then if method == "POST" then if validate_login(waf) then - -- 登录成功:记录验证认证 ngx_kv.ipCache:set(session_key, true, session_duration) - ngx_kv.ipCache:delete(login_attempts_key) -- 重置失败次数 + ngx_kv.ipCache:delete(login_attempts_key) return else - -- 登录失败:记录失败次数 login_attempts = login_attempts + 1 - ngx_kv.ipCache:set(login_attempts_key, login_attempts, 3600) -- 失败次数保存1小时 + ngx_kv.ipCache:set(login_attempts_key, login_attempts, 3600) - -- 可选:输出登录失败提示(直接返回页面避免请求到原站) local error_message = "用户名或密码错误,请重试。" ngx.header.content_type = "text/html; charset=utf-8" - return ngx.print(get_login_page(req_uri, error_message)) -- 使用直接返回页面 + return ngx.print(get_login_page(req_uri, error_message)) end else - -- 显示登录页面 ngx.header.content_type = "text/html; charset=utf-8" - return ngx.print(get_login_page(req_uri, nil)) -- 使用直接返回页面 + return ngx.print(get_login_page(req_uri, nil)) end end - -- 已经认证,继续处理后续请求 end return _M diff --git a/src/rules/third_party/brute-force-login-prevention.lua b/src/rules/third_party/brute-force-login-prevention.lua index 011c5a7..770f1ba 100644 --- a/src/rules/third_party/brute-force-login-prevention.lua +++ b/src/rules/third_party/brute-force-login-prevention.lua @@ -1,38 +1,39 @@ --[[ -规则名称: 防止爆破登录 +规则名称: 登录爆破防护 过滤阶段: 请求阶段 危险等级: 高危 -规则描述: 针对路径中包含登录、注册等关键词的URL特征,如果5分钟(300秒)内请求次数超过10次,则封禁该IP 1440分钟(24小时) +规则描述: 针对路径中包含登录、注册等关键词的URL进行防护 +作者: MCQSJ(https://github.com/MCQSJ) +更新日期: 2024/12/21 --]] +-- 配置参数 +local threshold = 30 -- 错误次数阈值 +local timeWindow = 180 -- 时间窗口,单位为秒 +local banDuration = 1440 * 60 -- 封禁时间,单位为秒 + local sh = waf.ipCache -local bruteForceKey = 'brute-force-login:' .. waf.ip -- 使用独立前缀标识,避免与其他规则冲突 +local bruteForceKey = 'brute-force-login:' .. waf.ip -- 定义特征路径关键词列表 local targetPaths = { "login", "signin", "signup", "register", "reset", "passwd", "account", "user" } --- 判断URI是否包含特征关键词 if not waf.pmMatch(waf.toLower(waf.uri), targetPaths) then - return false -- 如果路径中不包含任何特征关键词,则跳过检测 + return false end --- 获取缓存中的数据 local requestCount, flag = sh:get(bruteForceKey) if not requestCount then - -- 初始化计数,设置5分钟(300秒)的时间窗口 - sh:set(bruteForceKey, 1, 300, 1) + sh:set(bruteForceKey, 1, timeWindow, 1) else - -- 如果标志已经为2,则IP处于封禁状态,直接拦截 if flag == 2 then - return waf.block(true) -- 阻断请求 + return waf.block(true) end - -- 增加非法请求次数 sh:incr(bruteForceKey, 1) - if requestCount + 1 > 10 then - -- 达到爆破攻击检测阈值,标记为封禁状态,封禁时间为1440分钟(24小时) - sh:set(bruteForceKey, requestCount + 1, 86400, 2) - return true, "检测到登录接口发生爆破攻击,已封禁IP", true -- 日志载荷改为中文 + if requestCount + 1 > threshold then + sh:set(bruteForceKey, requestCount + 1, banDuration, 2) + return true, "检测到登录接口发生爆破攻击,已封禁IP", true end end diff --git a/src/rules/third_party/frequent-block-detection.lua b/src/rules/third_party/frequent-block-detection.lua index 7aab014..a3a7007 100644 --- a/src/rules/third_party/frequent-block-detection.lua +++ b/src/rules/third_party/frequent-block-detection.lua @@ -1,28 +1,31 @@ --[[ -规则名称: 频繁触发攻击拦截的IP拉黑 +规则名称: 高频攻击防护 过滤阶段: 请求阶段 危险等级: 高危 -规则描述: 检查当前请求的客户端IP是否在最近10分钟内频繁触发(超过30次)WAF的拦截,如果是,则拉黑IP 1440分钟,并记录日志。 -注意: 因为南墙WAF特性,此规则生效对规则ID有要求,需要将此规则与南墙自带规则的第一个规则交换位置才能生效。 +规则描述: 针对发起高频率攻击的行为进行防护 +作者: MCQSJ(https://github.com/MCQSJ) +更新日期: 2024/12/21 +!!!注意: 因为南墙WAF特性,此规则生效对规则ID有要求,需要将此规则与南墙自带规则的第一个规则交换位置才能生效!!! ]] -local sh = waf.ipCache -- 键值存储库,用于存储拉黑状态 -local ip_stats = waf.ipBlock -- 查询最近被南墙拦截的IP统计,如社区版本默认存储时间为10分钟 -local ip = waf.ip -local block_key = "blocked-" .. ip -- 用于记录IP拉黑状态的key +-- 配置参数 +local threshold = 60 -- 错误次数阈值 +local banDuration = 1440 * 60 -- 封禁时间,单位为秒 + +local sh = waf.ipCache +local ip_stats = waf.ipBlock +local ip = waf.ip +local block_key = "blocked-" .. ip --- 如果IP已经被拉黑则直接拦截 local c, f = sh:get(block_key) if c and f == 2 then - return waf.block(true) -- 重置TCP连接,不返回任何内容 + return waf.block(true) end --- 检查该IP在最近时间内是否频繁被拦截 local recent_count = ip_stats:get(ip) -if recent_count and recent_count > 30 then - -- 如果超过30次,则拉黑IP,设置1440分钟(24小时) - sh:set(block_key, 1, 86400, 2) -- 第三个参数86400为1440分钟(单位为秒),第四个参数2表示拉黑状态 - return true, "IP频繁触发拦截,已被拉黑", true -- 记录日志并拦截 +if recent_count and recent_count > threshold then + sh:set(block_key, 1, banDuration, 2) + return true, "IP频繁触发拦截,已被拉黑", true end return false diff --git a/src/rules/third_party/high-frequency-error-protection.lua b/src/rules/third_party/high-frequency-error-protection.lua index 410187a..b0a162f 100644 --- a/src/rules/third_party/high-frequency-error-protection.lua +++ b/src/rules/third_party/high-frequency-error-protection.lua @@ -2,11 +2,12 @@ 规则名称: 高频错误防护 过滤阶段: 返回HTTP头阶段 危险等级: 中危 -规则描述: 监测频繁返回400、401、403、404、405、429、444错误,当60秒内出现这些错误10次以上,则封禁1440分钟。 +规则描述: 针对频繁触发错误的请求的行为进行防护 +作者: MCQSJ(https://github.com/MCQSJ) +更新日期: 2024/12/21 --]] local function isSpecifiedError(status) - -- 检查是否为指定的状态码,限定在 [400, 401, 403, 404, 405, 429, 444] local allowed_errors = {400, 401, 403, 404, 405, 429, 444} return waf.inArray(status, allowed_errors) end @@ -16,36 +17,28 @@ local threshold = 10 -- 错误次数阈值 local timeWindow = 60 -- 时间窗口,单位为秒 local banDuration = 1440 * 60 -- 封禁时间,1440分钟 = 86400秒 --- 获取客户端IP local ip = waf.ip --- 获取返回的HTTP状态码 local status = waf.status --- 检查当前请求是否是指定的状态码错误,不是则直接返回false if not isSpecifiedError(status) then return false end --- 使用 waf.ipCache 记录当前 IP 的错误次数 local errorCache = waf.ipCache -local errorKey = "error:" .. ip -- 定义记录错误次数的键值,以 IP 为基础区分 +local errorKey = "error:" .. ip local errorCount, flag = errorCache:get(errorKey) --- 若当前记录不存在,初始化记录 if not errorCount then - errorCache:set(errorKey, 1, timeWindow) -- 初始错误计数设置为1,并设置为60秒过期 + errorCache:set(errorKey, 1, timeWindow) else if flag == 2 then - -- 标志为2表示该IP已被封禁,直接拦截,即刻终止 return waf.block(true) end - -- 累加错误计数 errorCache:incr(errorKey, 1) if errorCount + 1 >= threshold then - -- 达到错误频率阈值,标记当前IP为封禁状态 errorCache:set(errorKey, errorCount + 1, banDuration, 2) return true, "高频错误触发,IP已被封禁", true end diff --git a/src/rules/third_party/maintenance-mode.lua b/src/rules/third_party/maintenance-mode.lua deleted file mode 100644 index db14cea..0000000 --- a/src/rules/third_party/maintenance-mode.lua +++ /dev/null @@ -1,94 +0,0 @@ ---[[ -规则名称: 站点维护模式 -过滤阶段: 请求阶段 -危险等级: 低危 -规则描述: 将站点置为维护模式,返回“网页正在维护”的自定义页面。 ---]] - --- 检查是否启用维护模式的条件(可以根据需求自定义,以下为示例) -local maintenance_mode = true -- 可以通过配置文件或其他方式动态控制 - -if maintenance_mode then - -- 设置自定义的维护页面 HTML 内容 - local maintenance_html = [[ - - - - - 网页正在维护 - - - -
-
- - - - -
-

网页正在维护

-

抱歉,当前网页正在维护中,请稍后访问。

-
维护通知由 南墙 WAF 提供
-
- - ]] - - ngx.header.content_type = "text/html; charset=utf-8" - - -- 输出维护页面并终止请求处理 - ngx.print(maintenance_html) - return ngx.exit(ngx.HTTP_OK) -- 使用 ngx.HTTP_OK 结束请求,避免传递到源站 -end - -return false -- 未启用维护模式,不拦截请求