13 Star 56 Fork 25

bukale/zhongkui-waf

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
log_and_traffic.lua 12.32 KB
一键复制 编辑 原始数据 按行查看 历史
-- Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
-- Copyright (c) 2023 bukale bukale2022@163.com
local config = require "config"
local time = require "time"
local cjson = require "cjson.safe"
local stringutf8 = require "stringutf8"
local logger_factory = require "logger_factory"
local sql = require "sql"
local utils = require "utils"
local constants = require "constants"
local pairs = pairs
local upper = string.upper
local format = string.format
local sub = string.sub
local concat = table.concat
local default_if_blank = stringutf8.default_if_blank
local quote_sql_str = ngx.quote_sql_str
local cjson_encode = cjson.encode
local is_site_option_on = config.is_site_option_on
local is_system_option_on = config.is_system_option_on
local get_system_config = config.get_system_config
local write_sql_to_queue = sql.write_sql_to_queue
local LOG_PATH = config.LOG_PATH
local language = get_system_config('geoip').language ~= '' and get_system_config('geoip').language or 'en'
local function write_attack_log()
local ctx = ngx.ctx
local rule_table = ctx.rule_table
local data = ctx.hit_data
local action = ctx.action
local rule = rule_table.rule
local attack_type = rule_table.attackType
local severity_level = rule_table.severityLevel
local securityModule = ctx.module_name
local request_id = ctx.request_id
local geoip = ctx.geoip
local realIp = ctx.ip
local request_body = ctx.request_body
local response_body = ctx.response_body
local country = geoip.country
local province = geoip.province
local city = geoip.city
local country_name = country.names[language] or 'unknown'
local province_name = province.names[language] or 'unknown'
local city_name = city.names[language] or 'unknown'
local longitude = geoip.longitude
local latitude = geoip.latitude
local method = ngx.req.get_method()
local url = ngx.var.request_uri
local ua = ctx.ua
local host = default_if_blank(ngx.var.server_name, 'unknown')
local protocol = ngx.var.server_protocol
local referer = ngx.var.http_referer
local attackTime = ngx.localtime()
if is_system_option_on("attackLog") then
if get_system_config().attackLog.jsonFormat == "on" then
local log_table = {
request_id = request_id,
attack_type = attack_type,
ip = realIp,
ip_country = country_name,
ip_province = province_name,
ip_city = city_name,
ip_longitude = longitude,
ip_latitude = latitude,
attack_time = attackTime,
http_method = method,
server = host,
request_uri = url,
request_protocol = protocol,
request_data = data or '',
user_agent = ua,
hit_rule = rule,
action = action
}
local log_str, err = cjson_encode(log_table)
if log_str then
local host_logger = logger_factory.get_logger(LOG_PATH, host, true)
host_logger:log(log_str .. '\n')
else
ngx.log(ngx.ERR, "failed to encode json: ", err)
end
else
local address = country_name .. province_name .. city_name
address = default_if_blank(address, '-')
ua = default_if_blank(ua, '-')
data = default_if_blank(data, '-')
local log_str = concat({attack_type, realIp, address, "[" .. attackTime .. "]", '"' .. method, host, url, protocol .. '"', data, '"' .. ua .. '"', '"' .. rule .. '"', action},' ')
local host_logger = logger_factory.get_logger(LOG_PATH, host, true)
host_logger:log(log_str .. '\n')
end
end
if is_system_option_on("mysql") then
local sql_str = '(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %.7f, %.7f, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
local req_header = ngx.req.raw_header() or ''
if #req_header > 0 or request_body then
request_body = quote_sql_str(req_header .. (request_body or ''))
else
request_body = 'NULL'
end
local headers = ngx.resp.get_headers()
if headers or response_body then
local header = ''
for key, value in pairs(headers) do
header = header .. key .. ': ' .. value .. '\n'
end
if #header > 0 then
header = header .. '\n'
end
response_body = quote_sql_str(header .. (response_body or ''))
else
response_body = 'NULL'
end
if referer then
referer = quote_sql_str(referer)
else
referer = 'NULL'
end
sql_str = format(sql_str, quote_sql_str(request_id), quote_sql_str(realIp),
quote_sql_str(country.iso_code or ''), quote_sql_str(country.names['zh-CN'] or ''), quote_sql_str(country.names['en'] or ''),
quote_sql_str(province.iso_code or ''), quote_sql_str(province.names['zh-CN'] or ''), quote_sql_str(province.names['en'] or ''),
quote_sql_str(city.iso_code or ''), quote_sql_str(city.names['zh-CN'] or ''), quote_sql_str(city.names['en'] or ''),
longitude, latitude,
quote_sql_str(method), quote_sql_str(host), quote_sql_str(ua), referer, quote_sql_str(protocol), quote_sql_str(url), request_body,
quote_sql_str(ngx.status), response_body, quote_sql_str(attackTime), quote_sql_str(attack_type), quote_sql_str(severity_level), quote_sql_str(securityModule), quote_sql_str(sub(rule, 1, 500)), quote_sql_str(action))
write_sql_to_queue(constants.KEY_ATTACK_LOG, sql_str)
end
end
local function write_ip_block_log()
local ctx = ngx.ctx
local rule_table = ctx.rule_table
local attack_type = rule_table.attackType
local ip_block_expire_in_seconds = rule_table.ipBlockExpireInSeconds
local ip = ctx.ip
local action = ctx.action
if ip_block_expire_in_seconds == 0 then
local ipBlackLogger = logger_factory.get_logger(config.CONF_PATH .. "/global_rules/ipBlackList", 'ipBlack', false)
ipBlackLogger:log(ip .. "\n")
end
if is_system_option_on("mysql") then
local request_id = ctx.request_id
local geoip = ctx.geoip
local country = geoip.country
local province = geoip.province
local city = geoip.city
local longitude = geoip.longitude
local latitude = geoip.latitude
local start_time = ngx.localtime()
local endTime = 'NULL'
if ip_block_expire_in_seconds > 0 then
endTime = 'DATE_ADD(\'' .. start_time .. '\', INTERVAL ' .. ip_block_expire_in_seconds .. ' SECOND)'
end
local sql_str = '(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %.7f, %.7f, %s, %s, %u, %s, %s)'
sql_str = format(sql_str, quote_sql_str(request_id), quote_sql_str(ip),
quote_sql_str(country.iso_code or ''), quote_sql_str(country.names['zh-CN'] or ''), quote_sql_str(country.names['en'] or ''),
quote_sql_str(province.iso_code or ''), quote_sql_str(province.names['zh-CN'] or ''), quote_sql_str(province.names['en'] or ''),
quote_sql_str(city.iso_code or ''), quote_sql_str(city.names['zh-CN'] or ''), quote_sql_str(city.names['en'] or ''),
longitude, latitude,
quote_sql_str(attack_type), quote_sql_str(start_time), ip_block_expire_in_seconds, endTime, quote_sql_str(action))
write_sql_to_queue(constants.KEY_IP_BLOCK_LOG, sql_str)
else
local host_logger = logger_factory.get_logger(LOG_PATH .. "ipBlock.log", 'ipBlock', true)
host_logger:log(concat({ngx.localtime(), ip, attack_type, ip_block_expire_in_seconds .. 's'}, ' ') .. "\n")
end
end
local function get_ttl()
local ttl = ngx.ctx.ttl
if not ttl then
ttl = time.calculate_seconds_to_next_midnight()
ngx.ctx.ttl = ttl or 60
end
return ttl
end
local function count_waf_status()
local dict = ngx.shared.dict_req_count
local status = ngx.status
local ctx = ngx.ctx
if status > 399 and status < 500 then
utils.dict_incr(dict, constants.KEY_HTTP_4XX, get_ttl)
elseif status > 499 and status < 600 then
utils.dict_incr(dict, constants.KEY_HTTP_5XX, get_ttl)
end
utils.dict_incr(dict, constants.KEY_REQUEST_TIMES, get_ttl)
if ctx.is_attack then
utils.dict_incr(dict, constants.KEY_ATTACK_TIMES, get_ttl)
end
if ctx.is_blocked then
if ctx.is_attack then
if ctx.is_cc then
utils.dict_incr(dict, constants.KEY_BLOCK_TIMES_CC, get_ttl)
else
utils.dict_incr(dict, constants.KEY_BLOCK_TIMES_ATTACK, get_ttl)
end
elseif ctx.is_captcha then
if ctx.is_captcha_pass then
utils.dict_incr(dict, constants.KEY_CAPTCHA_PASS_TIMES, get_ttl)
else
utils.dict_incr(dict, constants.KEY_BLOCK_TIMES_CAPTCHA, get_ttl)
end
end
end
end
local function count_traffic_stats()
local ctx = ngx.ctx
local geoip = ctx.geoip
if geoip then
local country = geoip.country
local province = geoip.province
local city = geoip.city
local country_code = country.iso_code or ''
local country_cn = country.names['zh-CN'] or ''
local country_en = country.names['en'] or ''
local province_code = province.iso_code or ''
local province_cn = province.names['zh-CN'] or ''
local province_en = province.names['en'] or ''
local city_code = city.iso_code or ''
local city_cn = city.names['zh-CN'] or ''
local city_en = city.names['en'] or ''
local prefix = concat({country_code, country_cn, country_en, province_code, province_cn, province_en, city_code, city_cn, city_en}, '_') .. ':'
local dict = ngx.shared.dict_req_count_citys
utils.dict_incr(dict, prefix .. constants.KEY_REQUEST_TIMES, get_ttl)
if ctx.is_attack then
utils.dict_incr(dict, prefix .. constants.KEY_ATTACK_TIMES, get_ttl)
end
if ctx.is_blocked then
if ctx.is_attack then
if ctx.is_cc then
utils.dict_incr(dict, prefix .. constants.KEY_BLOCK_TIMES_CC, get_ttl)
else
utils.dict_incr(dict, prefix .. constants.KEY_BLOCK_TIMES_ATTACK, get_ttl)
end
elseif ctx.is_captcha then
if ctx.is_captcha_pass then
utils.dict_incr(dict, prefix .. constants.KEY_CAPTCHA_PASS_TIMES, get_ttl)
else
utils.dict_incr(dict, prefix .. constants.KEY_BLOCK_TIMES_CAPTCHA, get_ttl)
end
end
end
end
end
-- 按小时统计当天请求流量,存入缓存,key格式:2023-05-05 09
local function count_request_traffic()
local hour = time.get_date_hour()
local dict = ngx.shared.dict_req_count
utils.dict_incr(dict, hour, get_ttl)
end
--[[
按小时统计当天攻击请求流量,存入缓存,key格式:attack_2023-05-05 09
按天统计当天所有攻击类型流量,存入缓存,key格式:attack_type_2023-05-05_ARGS
]]
local function count_attack_request_traffic()
local rule_table = ngx.ctx.rule_table
local attack_type = upper(rule_table.attackType)
local dict = ngx.shared.dict_req_count
if attack_type ~= 'WHITEIP' then
local hour = time.get_date_hour()
utils.dict_incr(dict, constants.KEY_ATTACK_PREFIX .. hour, get_ttl)
end
local today = ngx.today() .. '_'
utils.dict_incr(dict, constants.KEY_ATTACK_TYPE_PREFIX .. today .. attack_type, get_ttl)
end
-- 按小时统计当天拦截请求流量,存入缓存,key格式:block_2023-05-05 09
local function count_block_request_traffic()
local dict = ngx.shared.dict_req_count
local hour = time.get_date_hour()
utils.dict_incr(dict, constants.KEY_BLOCKED_PREFIX .. hour, get_ttl)
end
if is_site_option_on("waf") then
count_request_traffic()
count_waf_status()
count_traffic_stats()
local ctx = ngx.ctx
if ctx.is_attack then
write_attack_log()
count_attack_request_traffic()
end
if ctx.is_blocked then
count_block_request_traffic()
end
if ctx.ip_blocked then
write_ip_block_log()
end
end
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Lua
1
https://gitee.com/bukaleyang/zhongkui-waf.git
git@gitee.com:bukaleyang/zhongkui-waf.git
bukaleyang
zhongkui-waf
zhongkui-waf
master

搜索帮助

0d507c66 1850385 C8b1a773 1850385