1 Star 0 Fork 7

王辉/easy_ngx_waf

forked from 克莱里昂/easy_ngx_waf 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
init.lua 33.26 KB
一键复制 编辑 原始数据 按行查看 历史
chleniang 提交于 2023-05-12 19:40 +08:00 . init
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
--- init_by_lua 阶段加载执行
--- 所有全局变量以 g_ 开头,请确保全局变量在除 本文件以外的其他代码中"只用不改"
--- 所有全局函数以 gfn_ 开头
local config = require("config")
local util = require("lib.util")
local rule_lib = require("lib.rule_core.rule")
local host_lib = require("lib.rule_core.host")
local post_lib = require("lib.post")
local string_util = require("lib.string_util")
local moses = require("lib.packages.moses_min")
-- 各个检测阶段的检测开关状态
local switch_on = {
ip_white = util.check_switch_is_on(config.ip_white_check),
ip_black = util.check_switch_is_on(config.ip_black_check),
ua_white = util.check_switch_is_on(config.user_agent_white_check),
ua = util.check_switch_is_on(config.user_agent_check),
url = util.check_switch_is_on(config.url_check),
url_white = util.check_switch_is_on(config.url_white_check),
seo_spider = util.check_switch_is_on(config.seo_spider_check),
ip_foreign = util.check_switch_is_on(config.ip_foreign_check),
cc = util.check_switch_is_on(config.cc_check),
cookie = util.check_switch_is_on(config.cookie_check),
args = util.check_switch_is_on(config.args_check),
post = util.check_switch_is_on(config.post_check),
header = util.check_switch_is_on(config.header_check),
}
-- waf日志文件handle 全局变量
g_log_file_handle = io.open(config.waf_log_file, "a+")
if not g_log_file_handle then
ngx.log(ngx.ERR, "[easy_waf] easy_ngx_waf log file open failed at init phase.")
end
-- waf总开关 全局变量
g_waf_enable = util.check_switch_is_on(config.waf_enable)
-- 读取加载所有WAF规则到 全局变量
g_rules = rule_lib.load_all_rules(true)
-- 读取加载所有蜘蛛规则数据到 全局变量
g_spider_data = rule_lib.load_spider_data()
---将规则value转化为字符串,以便日志记录(有些规则value是数组)
---@param rule_value any
---@return string
local function rule_val_to_str(rule_value)
if type(rule_value) == "table" then
return "[" .. table.concat(rule_value, ",") .. "]"
end
return "(" .. tostring(rule_value) .. ")"
end
---执行规则校验;
--- 校验 g_rules[rule_key][rule_act] 规则列表中每一条规则,
--- 命中任何一条即刻返回true;
--- 没有对应规则列表或者所有规则都没命中返回false;
---@param rule_key string 规则主键
---@param rule_act string 规则act "deny"/"allow"/"next"
---@param var_value any 具体要校验的客户端来访值(根据规则变量var获取到的)
---@return boolean
local function do_check(rule_key, rule_act, var_value)
if g_rules[rule_key] and g_rules[rule_key][rule_act] then
if rule_act == "next" then
for _, rule in ipairs(g_rules[rule_key][rule_act]) do
-- act:next 只有在 rule["log"]="on" 时才有意义,再运算执行
if util.check_switch_is_on(rule["log"]) then
local var_v
local msg_var = rule["var"]
if rule["var_s"] and var_value[rule["var_s"]] then
var_v = var_value[rule["var_s"]]
msg_var = rule["var"] .. ":" .. rule["var_s"]
elseif type(var_value) == "table" then
var_v = ""
for _, v in pairs(var_value) do
var_v = var_v .. " " .. v
end
else
var_v = var_value
end
local res, err_info = rule_lib.execute(var_v, rule["op"], rule["value"])
if res then
util.waf_log("[rule:" .. rule_key .. ":" .. rule_act .. "] ezv_"
.. rule_val_to_str(var_v) .. "_ezv var_"
.. rule_val_to_str(msg_var) .. "_var rule_"
.. rule_val_to_str(rule["value"]) .. "_rule "
.. (rule["msg"] or "")
)
return true
elseif res == nil then
ngx.log(ngx.ERR,
"[easy_waf] " ..
rule_key .. ":" .. rule_act .. " execute(nil) reason:" .. tostring(err_info))
end
end
end
else
-- act:allow/deny
for _, rule in ipairs(g_rules[rule_key][rule_act]) do
local var_v
local msg_var = rule["var"]
if rule["var_s"] and var_value[rule["var_s"]] then
var_v = var_value[rule["var_s"]]
msg_var = rule["var"] .. ":" .. rule["var_s"]
elseif type(var_value) == "table" then
var_v = ""
for _, v in pairs(var_value) do
var_v = var_v .. " " .. v
end
else
var_v = var_value
end
local res, err_info = rule_lib.execute(var_v, rule["op"], rule["value"])
if res then
if util.check_switch_is_on(rule["log"]) then
util.waf_log("[rule:" .. rule_key .. ":" .. rule_act .. "] ezv_"
.. rule_val_to_str(var_v) .. "_ezv var_"
.. rule_val_to_str(msg_var) .. "_var rule_"
.. rule_val_to_str(rule["value"]) .. "_rule "
.. (rule["msg"] or "")
)
end
return true
elseif res == nil then
ngx.log(ngx.ERR,
"[easy_waf] " .. rule_key .. ":" .. rule_act .. " execute(nil) reason:" .. tostring(err_info))
end
end
end
end
return false
end
-- IP CIDR工具类
g_ipmatcher = nil
if (require("ffi")).os == "Windows" then
g_ipmatcher = require("lib.packages.ipmatcher_win")
else
g_ipmatcher = require("lib.packages.ipmatcher")
end
---来访IP扫描(没有act),只针对log:on的规则进行日志记录
---@param client_ip string 来访IP
gfn_ip_next_check = function(client_ip)
do_check("ip", "next", client_ip)
end
---来访IP在白名单内返回true, 未开启白名单检测功能/来访IP不在名单内,返回false
---@param client_ip string 来访IP
---@param host string 被访主机
---@return boolean
gfn_ip_white_check = function(client_ip, host)
if switch_on.ip_white
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_IP_WHITE))
then
local res = do_check("ip", "allow", client_ip)
if res then
return res
end
end
return false
end
---来访IP在黑名单内返回true, 未开启黑名单检测功能/来访IP不在名单内,返回false
---@param client_ip string 来访IP
---@param host string 被访主机
---@return boolean
gfn_ip_black_check = function(client_ip, host)
if switch_on.ip_black
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_IP_BLACK))
then
local res = do_check("ip", "deny", client_ip)
if res then
return res
end
end
return false
end
---来访UA+IP在蜘蛛规则名单内返回true, 未开启UA蜘蛛检测功能/来访UA+IP不在蜘蛛规则名单内,返回false
---@param ua string user-agent
---@param client_ip string 来访IP
---@return boolean
gfn_seo_spider_check = function(ua, client_ip)
if switch_on.seo_spider then
local is_seo, seo_name = rule_lib.check_is_seo(ua, client_ip)
if is_seo and util.check_switch_is_on(config.seo_spider_log) then
util.waf_log("[rule:seo_spider] (" .. (seo_name or "unknown") .. ")")
end
return is_seo
end
return false
end
-- 加载libmaxminddb以及Country ip库
-- 全局变量 g_maxminddb
g_maxminddb = require("lib.packages.maxminddb")
if not g_maxminddb.initted() then
g_maxminddb.init(config.waf_base_path .. "/data/Country.mmdb")
end
---来访IP是境外IP返回true, 未开启境外IP检测功能/来访IP非境外IP,返回false
---@param client_ip string 来访IP
---@param host string 被访主机
---@return boolean
gfn_ip_foreign_check = function(client_ip, host)
if switch_on.ip_foreign
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_IP_FOREIGN))
then
-- support ipv6 e.g. 2001:4860:0:1001::3004:ef68
local res, err = g_maxminddb.lookup(client_ip)
if not res then
ngx.log(ngx.ERR, "[easy_waf] failed to LOOKUP ip:(" .. client_ip .. ") reason:" .. tostring(err))
return false
end
-- res -> table {"country":{"iso_code":"CN"}}
if res.country and res.country.iso_code then
local country_code = res.country.iso_code
for _, v in ipairs(config.domestic_iso_code) do
if country_code == v then
-- 在 非境外地区列表中
return false
end
end
-- 不在 非境外地区列表中
if util.check_switch_is_on(config.ip_foreign_log) then
util.waf_log("[rule:ip_foreign] (" .. country_code .. ")")
end
return true
end
end
return false
end
---来访UA在白名单内返回true, 未开启UA白名单检测功能/来访UA不在白名单内,返回false
---@param ua string 来访user-agent
---@param host string 被访主机
---@return boolean
gfn_user_agent_white_check = function(ua, host)
if switch_on.ua_white
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_UA))
then
local res = do_check("ua", "allow", ua)
if res then
return res
end
end
return false
end
---来访UA在规则名单内返回true, 未开启UA检测功能/来访UA不在规则名单内,返回false
---@param ua string 来访user-agent
---@param host string 被访主机
---@return boolean
gfn_user_agent_check = function(ua, host)
if switch_on.ua
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_UA))
then
-- 针对next的校验匹配,不用关心结果
do_check("ua", "next", ua)
local res = do_check("ua", "deny", ua)
if res then
return res
end
end
return false
end
---URL在规则白名单内返回true, 未开启URL白名单检测功能/来访URL不在规则名单内,返回false;
---@param request_uri string 请求URI
---@param request_filename string 请求的具体文件
---@return boolean
gfn_url_white_check = function(request_uri, request_filename)
if switch_on.url_white then
-- URL检测阶段需要检测的规则主键有:
-- "request_uri","request_uri_raw","request_filename"
local res = do_check("request_uri", "allow", request_uri)
if res then
return res
end
-- 白名单太敏感,不做以下两个规则分类的 allow 校验
-- "request_uri_raw", "request_filename"
end
return false
end
---URL在规则内返回true, 未开启URL检测功能/来访URL不在规则名单内,返回false
---@param request_uri string 请求URI
---@param request_filename string 请求的具体文件
---@return boolean
gfn_url_check = function(request_uri, request_filename)
if switch_on.url then
-- URL检测阶段需要检测的规则主键有:
-- "request_uri","request_uri_raw","request_filename"
-- 针对next的校验匹配,不用关心结果
do_check("request_uri", "next", request_uri)
do_check("request_uri_raw", "next", ngx.unescape_uri(request_uri))
do_check("request_filename", "next", ngx.unescape_uri(request_filename))
local res = do_check("request_uri", "deny", request_uri)
if res then
return res
end
res = do_check("request_uri_raw", "deny", ngx.unescape_uri(request_uri))
if res then
return res
end
res = do_check("request_filename", "deny", ngx.unescape_uri(request_filename))
if res then
return res
end
end
return false
end
-- 全局变量
g_cc_size = tonumber(string.match(config.cc_rate, "(.*)/"))
g_cc_window = tonumber(string.match(config.cc_rate, "/(.*)"))
g_cckey_prefix = "ez:cckey:"
g_ccip_prefix = "ez:ccip:"
local ccutil = require("lib.ccutil")
ccutil.init()
---CC检测
---@param ip string 来访IP
---@param uri string 请求URI(不含参数)
---@param host string 被访主机
---@return boolean
gfn_cc_check = function(ip, uri, host)
if switch_on.cc
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_CC))
then
if ccutil.ccip_exist(ip) then
return true
else
-- 防止uri过长在存储时出现意外情况,在此md5
local token = g_cckey_prefix .. ip .. ":" .. ngx.md5(uri)
local res = ccutil.get(token)
if res ~= nil then
local token_count = tonumber(res)
if token_count >= g_cc_size then
ccutil.set_ccip(ip, config.cc_ip_lock_time)
util.waf_log("[rule:cc_deny]")
return true
else
ccutil.incr(token)
end
else
ccutil.set(token, 1, g_cc_window)
end
end
end
return false
end
---cookie在规则内返回true, 未开启cookie检测功能/来访cookie不在规则名单内,返回false
---@param host string 被访主机
---@return boolean
gfn_cookie_check = function(host)
if switch_on.cookie
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_COOKIE))
then
-- cookie检测阶段需要检测的规则主键有:
-- "cookie","request_cookies","request_cookies_names"
local cookie_str = ngx.var.http_cookie
if not cookie_str then
return false
end
local cookie_tbl = util.cookie_string_to_table(cookie_str)
for _, cookie in ipairs(cookie_tbl) do
-- 针对next的校验匹配,不用关心结果
do_check("cookie", "next", cookie["v"])
do_check("request_cookies", "next", cookie["v"])
do_check("request_cookies_names", "next", cookie["k"])
end
local res
for _, cookie in ipairs(cookie_tbl) do
res = do_check("cookie", "deny", cookie["v"])
if res then
return res
end
res = do_check("request_cookies", "deny", cookie["v"])
if res then
return res
end
res = do_check("request_cookies_names", "deny", cookie["k"])
if res then
return res
end
end
end
return false
end
---args在规则内返回true, 未开启args检测功能/args不在规则名单内,返回false;
---校验所有 args_name 以及 args 参数值; 忽略所有act:allow的规则;
---@param host string 被访主机
---@return boolean
gfn_args_check = function(host)
if switch_on.args
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_ARGS))
then
-- args检测阶段需要检测的规则主键有:
-- "query_string","args_get","args_get_names"
if g_rules["query_string"] then
-- query_string 是原始提供的,不进行URL解码
local query_string = ngx.var.query_string
if query_string then
local s = ngx.unescape_uri(query_string)
-- 针对next的校验匹配,不用关心结果
do_check("query_string", "next", s)
local res = do_check("query_string", "deny", s)
if res then
return res
end
end
end
local args = ngx.req.get_uri_args()
-- 针对args act:next相关的规则应该是极少数情况,在此先作判断再循环
if g_rules["args_get"]["next"]
or g_rules["args_get_names"]["next"]
then
for args_name, args_value in pairs(args) do
-- 针对 args_value 参数值进行处理先
local args_data = util.to_string(args_value, " ")
-- 针对next的校验匹配,不用关心结果
do_check("args_get", "next", ngx.unescape_uri(args_data))
do_check("args_get_names", "next", ngx.unescape_uri(args_name))
end
end
for args_name, args_value in pairs(args) do
local res = do_check("args_get_names", "deny", ngx.unescape_uri(args_name))
if res then
return res
end
-- 针对 args_value 参数值进行处理先
local args_data = util.to_string(args_value, " ")
res = do_check("args_get", "deny", ngx.unescape_uri(args_data))
if res then
return res
end
end -- END args 循环
end
return false
end
---post在规则内返回true, 未开启post检测功能/post不在规则名单内,返回false;
---校验所有 post_name 以及 post 参数值; 忽略所有act:allow的规则;
---@param host string 被访主机
---@return boolean
gfn_post_check = function(host)
if (ngx.req.get_method() == "POST")
and switch_on.post
and (not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_POST))
then
-- post参数检测阶段需要检测的规则主键有:
-- "args_post","args_post_names" 所有POST都检测
-- "request_body" 只有 application/x-www-form-urlencoded
-- "files","files_names" 只有 multipart/form-data
local content_type = ngx.var.content_type
if not content_type then
return false
end
local ct = post_lib.parse_content_type(content_type)
if ct == post_lib.CONTENT_TYPE_WWW_FORM_URLENCODED then
-- Content-Type: application/x-www-form-urlencoded
ngx.req.read_body()
local post_args = ngx.req.get_post_args()
if not post_args then
return false
end
-- 针对 act:next 相关的规则应该是极少数情况,在此先作判断再循环
if g_rules["args_post_names"]["next"] then
-- 针对 args_post_names 参数名的校验
for post_name, _ in pairs(post_args) do
do_check("args_post_names", "next", ngx.unescape_uri(post_name))
end
end
if g_rules["args_post"]["next"] then
-- 针对 args_post 参数值的校验
for _, post_value in pairs(post_args) do
local post_data = util.to_string(post_value, " ")
-- 针对next的校验匹配,不用关心结果
do_check("args_post", "next", ngx.unescape_uri(post_data))
end
end
-- 针对 act:deny 的校验
if g_rules["args_post_names"]["deny"] or g_rules["args_post"]["deny"] then
for post_name, post_value in pairs(post_args) do
local res = do_check("args_post_names", "deny", ngx.unescape_uri(post_name))
if res then
return res
end
-- 针对 args_post 参数值的校验
local post_data = util.to_string(post_value, " ")
res = do_check("args_post", "deny", ngx.unescape_uri(post_data))
if res then
return res
end
end
end
-- "request_body" 包含原始请求体 当检测到 "application/x-www-form-urlencoded" 内容类型时
if g_rules["request_body"]
and (
g_rules["request_body"]["next"]
or g_rules["request_body"]["deny"]
)
then
local request_body = ngx.var.request_body
if request_body then
do_check("request_body", "next", ngx.unescape_uri(request_body))
local res = do_check("request_body", "deny", ngx.unescape_uri(request_body))
if res then
return res
end
end
end
elseif ct == post_lib.CONTENT_TYPE_FORM_DATA then
-- Content-Type: multipart/form-data
ngx.req.read_body()
local content_len = tonumber(ngx.var.content_length)
if (not content_len) or (content_len < 1) then
return false
end
-- 解析出 boundary 字符串
local boundary = post_lib.parse_form_data_boundary(content_type)
if not boundary then
return false
end
local body_data = ngx.req.get_body_data()
local form_data = {
file = {},
field = {},
}
local tmp_field_data = {}
if body_data then
local is_file = false
local is_field = false
local has_split_empty = false
for line in string.gmatch(body_data, ".-\r?\n") do
-- 一行一行处理
local line_str = string_util.trim(line)
local is_boundary_line = post_lib.is_form_data_boundary_line(boundary, line_str)
if is_boundary_line then
if is_file then
table.insert(form_data["file"], moses.clone(tmp_field_data))
elseif is_field then
table.insert(form_data["field"], moses.clone(tmp_field_data))
end
-- 重置
tmp_field_data = {}
is_file = false
is_field = false
has_split_empty = false
elseif not is_file then
-- 文件内容此处未考虑验证,在已经确认是上传文件的情况下不处理行内容
local line_type, info = post_lib.parse_form_data_line(boundary, line_str)
if line_type == post_lib.FORM_DATA_LINE_TYPE_FILE and info then
tmp_field_data = {
filename = (info["filename"] or ""),
name = (info["name"] or ""),
}
is_file = true
elseif line_type == post_lib.FORM_DATA_LINE_TYPE_FIELD and info then
tmp_field_data = {
name = (info["name"] or ""),
value = {}
}
is_field = true
elseif line_type == post_lib.FORM_DATA_LINE_EMPTY then
has_split_empty = true
elseif is_field and has_split_empty then
if tmp_field_data["value"] then
table.insert(tmp_field_data["value"], line_str)
else
tmp_field_data["value"] = { line_str }
end
end
end
end
else
-- 通过临时文件获取body_data
local body_file = ngx.req.get_body_file()
if not body_file then
return false
end
local fh, err = io.open(body_file, "r")
if fh then
local is_file = false
local is_field = false
local has_split_empty = false
fh:seek("set")
local line = fh:read("*l")
while line do
-- 一行一行处理
local is_boundary_line = post_lib.is_form_data_boundary_line(boundary, line)
if is_boundary_line then
if is_file then
table.insert(form_data["file"], moses.clone(tmp_field_data))
elseif is_field then
table.insert(form_data["field"], moses.clone(tmp_field_data))
end
-- 重置
tmp_field_data = {}
is_file = false
is_field = false
has_split_empty = false
elseif not is_file then
-- 文件内容此处未考虑验证,在已经确认是上传文件的情况下不处理行内容
local line_type, info = post_lib.parse_form_data_line(boundary, line)
if line_type == post_lib.FORM_DATA_LINE_TYPE_FILE and info then
tmp_field_data = {
filename = (info["filename"] or ""),
name = (info["name"] or ""),
}
is_file = true
elseif line_type == post_lib.FORM_DATA_LINE_TYPE_FIELD and info then
tmp_field_data = {
name = (info["name"] or ""),
value = {}
}
is_field = true
elseif line_type == post_lib.FORM_DATA_LINE_EMPTY then
has_split_empty = true
elseif is_field and has_split_empty then
if tmp_field_data["value"] then
table.insert(tmp_field_data["value"], line)
else
tmp_field_data["value"] = { line }
end
end
end
line = fh:read("*l")
end
fh:close()
else
ngx.log(ngx.ERR, "[easy_waf] get_body_file() open failed:" .. tostring(err))
return false
end
end
-- 针对 act:next 相关的规则应该是极少数情况,在此先作判断再循环
if g_rules["args_post_names"]["next"] or g_rules["args_post"]["next"] then
for _, item in ipairs(form_data["field"]) do
do_check("args_post_names", "next", ngx.unescape_uri(item["name"]))
do_check("args_post", "next", ngx.unescape_uri(table.concat(item["value"], " ")))
end
end
-- act:deny
for _, item in ipairs(form_data["field"]) do
local res =
do_check("args_post_names",
"deny",
ngx.unescape_uri(item["name"])
)
if res then
return res
end
res =
do_check("args_post",
"deny",
ngx.unescape_uri(table.concat(item["value"], " "))
)
if res then
return res
end
end
-- files:上传文件原始名 files_names:上传文件表单名
if g_rules["files"]["next"] or g_rules["files_names"]["next"] then
for _, item in ipairs(form_data["file"]) do
do_check("files", "next", ngx.unescape_uri(item["filename"]))
do_check("files_names", "next", ngx.unescape_uri(item["name"]))
end
end
if g_rules["files"]["deny"] or g_rules["files_names"]["deny"] then
for _, item in ipairs(form_data["file"]) do
local res = do_check("files", "deny", ngx.unescape_uri(item["filename"]))
if res then
return res
end
res = do_check("files_names", "deny", ngx.unescape_uri(item["name"]))
if res then
return res
end
end
end
elseif ct == post_lib.CONTENT_TYPE_JSON then
-- Content-Type: application/json
ngx.req.read_body()
local cjson = require("cjson")
local body_data = ngx.req.get_body_data()
if not body_data then
return false
end
local success, result = pcall(cjson.decode, body_data)
if not success then
return false
end
local res_type = type(result)
if res_type == "table" then
-- 针对 act:next 相关的规则应该是极少数情况,在此先作判断再循环
if g_rules["args_post_names"]["next"] or g_rules["args_post"]["next"] then
-- 针对 args_post_names args_post 的校验
for post_name, post_value in pairs(result) do
do_check("args_post_names", "next", ngx.unescape_uri(post_name))
local post_data = util.to_string(post_value, " ")
do_check("args_post", "next", ngx.unescape_uri(post_data))
end
end
-- 针对 act:deny 的校验
if g_rules["args_post_names"]["deny"] or g_rules["args_post"]["deny"] then
for post_name, post_value in pairs(result) do
local res = do_check("args_post_names", "deny", ngx.unescape_uri(post_name))
if res then
return res
end
-- 针对 args_post 参数值的校验
local post_data = util.to_string(post_value, " ")
res = do_check("args_post", "deny", ngx.unescape_uri(post_data))
if res then
return res
end
end
end
elseif res_type == "string" then
do_check("args_post", "next", ngx.unescape_uri(result))
local res = do_check("args_post", "deny", ngx.unescape_uri(result))
if res then
return res
end
end
else
-- Content-Type: 其他类型
ngx.req.read_body()
local body_data = ngx.req.get_body_data()
if not body_data then
return false
end
-- 针对 act:deny 的校验
local res = do_check("args_post_names", "deny", ngx.unescape_uri(body_data))
if res then
return res
end
res = do_check("args_post", "deny", ngx.unescape_uri(body_data))
if res then
return res
end
end
end
return false
end
---header规则校验(不含user-agent cookie)
--- 校验命中返回true, 否则返回false;
--- 在规则内返回true, 未开启header检测功能/header不在规则名单内,返回false;
---忽略所有act:allow的规则;
---@param host string 被访主机
---@return boolean
gfn_header_check = function(host)
if (switch_on.header and
(not host_lib.is_ex_host(host, host_lib.XHOST_SCOPE_HEADER))
)
then
-- header检测阶段需要检测的规则主键有:
-- "request_headers_names","request_headers"
if g_rules["request_headers_names"]["next"]
or g_rules["request_headers"]["next"]
or g_rules["request_headers_names"]["deny"]
or g_rules["request_headers"]["deny"]
then
local headers = ngx.req.get_headers(100)
if headers["user_agent"] then
headers["user_agent"] = nil
end
if headers["cookie"] then
headers["cookie"] = nil
end
local headers_names = {}
for k, _ in pairs(headers) do
table.insert(headers_names, k)
end
do_check("request_headers_names", "next", headers_names)
do_check("request_headers", "next", headers)
local res = do_check("request_headers_names", "deny", headers_names)
if res then
return res
end
res = do_check("request_headers", "deny", headers)
if res then
return res
end
end
end
return false
end
---拦截响应
---@param resp number | string 拦截响应状态码/响应html
gfn_deny_response = function(resp)
local return_type = type(resp)
if return_type == "number" then
ngx.status = resp
return ngx.exit(resp)
elseif resp == "default" then
-- 状态码设置要先于 say() 否则会有 error log
ngx.status = config.default_return_code
ngx.say(config.default_return_html)
return ngx.exit(ngx.HTTP_OK)
else
ngx.status = config.default_return_code
ngx.say(resp)
return ngx.exit(ngx.HTTP_OK)
end
end
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Lua
1
https://gitee.com/hzdwang/easy_ngx_waf.git
git@gitee.com:hzdwang/easy_ngx_waf.git
hzdwang
easy_ngx_waf
easy_ngx_waf
master

搜索帮助