2023-02-06 15:53:30 +08:00
|
|
|
local match = string.match
|
|
|
|
local ngxmatch=ngx.re.match
|
|
|
|
local unescape=ngx.unescape_uri
|
|
|
|
local get_headers = ngx.req.get_headers
|
|
|
|
local cjson = require "cjson"
|
|
|
|
local content_length=tonumber(ngx.req.get_headers()['content-length'])
|
|
|
|
local method=ngx.req.get_method()
|
|
|
|
|
|
|
|
|
|
|
|
local function optionIsOn(options)
|
2023-02-20 16:31:16 +08:00
|
|
|
return options == "on" or options == "On" or options == "ON"
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local logpath = ngx.var.logdir
|
|
|
|
local rulepath = ngx.var.RulePath
|
|
|
|
local attacklog = optionIsOn(ngx.var.attackLog)
|
|
|
|
local Redirect=optionIsOn(ngx.var.redirect)
|
|
|
|
local CCDeny = optionIsOn(ngx.var.CCDeny)
|
|
|
|
local UrlBlockDeny = optionIsOn(ngx.var.urlBlockDeny)
|
|
|
|
local UrlWhiteAllow = optionIsOn(ngx.var.urlWhiteAllow)
|
|
|
|
local IpBlockDeny = optionIsOn(ngx.var.ipBlockDeny)
|
|
|
|
local IpWhiteAllow = optionIsOn(ngx.var.ipWhiteAllow)
|
|
|
|
local PostDeny = optionIsOn(ngx.var.postDeny)
|
|
|
|
local ArgsDeny = optionIsOn(ngx.var.argsDeny)
|
|
|
|
local CookieDeny = optionIsOn(ngx.var.cookieDeny)
|
|
|
|
local FileExtDeny = optionIsOn(ngx.var.fileExtDeny)
|
|
|
|
|
|
|
|
local function getClientIp()
|
2023-02-20 16:31:16 +08:00
|
|
|
IP = ngx.var.remote_addr
|
|
|
|
if IP == nil then
|
|
|
|
IP = "unknown"
|
|
|
|
end
|
|
|
|
return IP
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
local function write(logfile,msg)
|
2023-02-20 16:31:16 +08:00
|
|
|
local fd = io.open(logfile,"ab")
|
|
|
|
if fd == nil then return end
|
|
|
|
fd:write(msg)
|
|
|
|
fd:flush()
|
|
|
|
fd:close()
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
local function log(method,url,data,ruletag)
|
2023-02-20 16:31:16 +08:00
|
|
|
if attacklog then
|
|
|
|
local realIp = getClientIp()
|
|
|
|
local ua = ngx.var.http_user_agent
|
|
|
|
local servername=ngx.var.server_name
|
|
|
|
local time=ngx.localtime()
|
2023-02-06 15:53:30 +08:00
|
|
|
local line = nil
|
2023-02-20 16:31:16 +08:00
|
|
|
if ua then
|
|
|
|
line = realIp.." ["..time.."] \""..method.." "..servername..url.."\" \""..data.."\" \""..ua.."\" \""..ruletag.."\"\n"
|
|
|
|
else
|
|
|
|
line = realIp.." ["..time.."] \""..method.." "..servername..url.."\" \""..data.."\" - \""..ruletag.."\"\n"
|
|
|
|
end
|
|
|
|
local filename = logpath..'/'..servername.."_"..ngx.today().."_sec.log"
|
|
|
|
write(filename,line)
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
------------------------------------规则读取函数-------------------------------------------------------------------
|
|
|
|
local function read_json(var)
|
2023-02-20 16:31:16 +08:00
|
|
|
file = io.open(rulepath..'/'..var .. '.json',"r")
|
|
|
|
if file==nil then
|
|
|
|
return
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
str = file:read("*a")
|
|
|
|
file:close()
|
|
|
|
list = cjson.decode(str)
|
|
|
|
return list
|
|
|
|
end
|
|
|
|
|
2023-02-20 16:31:16 +08:00
|
|
|
local function select_rules(rules)
|
|
|
|
if not rules then return {} end
|
|
|
|
new_rules = {}
|
|
|
|
for i,v in ipairs(rules) do
|
2023-02-22 18:53:48 +08:00
|
|
|
if v[3] == 1 then
|
|
|
|
table.insert(new_rules,v[1])
|
2023-02-20 16:31:16 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
return new_rules
|
|
|
|
end
|
|
|
|
|
2023-02-06 15:53:30 +08:00
|
|
|
local function read_str(var)
|
|
|
|
file = io.open(rulepath..'/'..var,"r")
|
2023-02-20 16:31:16 +08:00
|
|
|
if file==nil then
|
|
|
|
return
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
local str = file:read("*a")
|
|
|
|
file:close()
|
2023-02-20 16:31:16 +08:00
|
|
|
return str
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
2023-02-20 16:31:16 +08:00
|
|
|
local argsCheckList=select_rules(read_json('args_check'))
|
|
|
|
local postCheckList=select_rules(read_json('post_check'))
|
|
|
|
local cookieBlockList=select_rules(read_json('cookie_block'))
|
|
|
|
local uarules=select_rules(read_json('user_agent'))
|
2023-02-06 15:53:30 +08:00
|
|
|
|
2023-02-20 16:31:16 +08:00
|
|
|
local urlWhiteList=read_json('url_white')
|
|
|
|
local urlBlockList=read_json('url_block')
|
|
|
|
local ipWhiteList=read_json('ip_white')
|
|
|
|
local ipBlockList=read_json('ip_block')
|
|
|
|
local fileExtBlockList = read_json('file_ext_block')
|
2023-02-06 15:53:30 +08:00
|
|
|
|
2023-02-20 16:31:16 +08:00
|
|
|
local ccRate=read_str('cc.json')
|
2023-02-06 15:53:30 +08:00
|
|
|
local html=read_str('html')
|
|
|
|
|
|
|
|
local function say_html()
|
2023-02-20 16:31:16 +08:00
|
|
|
if Redirect then
|
|
|
|
ngx.header.content_type = "text/html"
|
|
|
|
ngx.status = ngx.HTTP_FORBIDDEN
|
|
|
|
ngx.say(html)
|
|
|
|
ngx.exit(ngx.status)
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function whiteurl()
|
2023-02-20 16:31:16 +08:00
|
|
|
if UrlWhiteAllow then
|
|
|
|
if urlWhiteList ~=nil then
|
|
|
|
for _,rule in pairs(urlWhiteList) do
|
|
|
|
if ngxmatch(ngx.var.uri,rule,"isjo") then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
local function fileExtCheck(ext)
|
2023-02-20 16:31:16 +08:00
|
|
|
if FileExtDeny then
|
|
|
|
local items = Set(fileExtBlockList)
|
|
|
|
ext=string.lower(ext)
|
|
|
|
if ext then
|
|
|
|
for rule in pairs(items) do
|
|
|
|
if ngx.re.match(ext,rule,"isjo") then
|
|
|
|
log('POST',ngx.var.request_uri,"-","file attack with ext "..ext)
|
|
|
|
say_html()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
function Set (list)
|
2023-02-20 16:31:16 +08:00
|
|
|
local set = {}
|
|
|
|
for _, l in ipairs(list) do set[l] = true end
|
|
|
|
return set
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function args()
|
2023-02-20 16:31:16 +08:00
|
|
|
if ArgsDeny then
|
|
|
|
if argsCheckList then
|
|
|
|
for _,rule in pairs(argsCheckList) do
|
|
|
|
local uriArgs = ngx.req.get_uri_args()
|
|
|
|
for key, val in pairs(uriArgs) do
|
|
|
|
if type(val)=='table' then
|
|
|
|
local t={}
|
|
|
|
for k,v in pairs(val) do
|
|
|
|
if v == true then
|
|
|
|
v=""
|
|
|
|
end
|
|
|
|
table.insert(t,v)
|
|
|
|
end
|
|
|
|
data=table.concat(t, " ")
|
|
|
|
else
|
|
|
|
data=val
|
|
|
|
end
|
|
|
|
if data and type(data) ~= "boolean" and rule ~="" and ngxmatch(unescape(data),rule,"isjo") then
|
|
|
|
log('GET',ngx.var.request_uri,"-",rule)
|
|
|
|
say_html()
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function url()
|
2023-02-20 16:31:16 +08:00
|
|
|
if UrlBlockDeny then
|
|
|
|
for _,rule in pairs(urlBlockList) do
|
|
|
|
if rule ~="" and ngxmatch(ngx.var.request_uri,rule,"isjo") then
|
|
|
|
log('GET',ngx.var.request_uri,"-",rule)
|
|
|
|
say_html()
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
function ua()
|
2023-02-20 16:31:16 +08:00
|
|
|
local ua = ngx.var.http_user_agent
|
|
|
|
if ua ~= nil then
|
|
|
|
for _,rule in pairs(uarules) do
|
|
|
|
if rule ~="" and ngxmatch(ua,rule,"isjo") then
|
|
|
|
log('UA',ngx.var.request_uri,"-",rule)
|
|
|
|
say_html()
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
function body(data)
|
2023-02-20 16:31:16 +08:00
|
|
|
for _,rule in pairs(postCheckList) do
|
|
|
|
if rule ~="" and data~="" and ngxmatch(unescape(data),rule,"isjo") then
|
|
|
|
log('POST',ngx.var.request_uri,data,rule)
|
|
|
|
say_html()
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
local function cookie()
|
2023-02-20 16:31:16 +08:00
|
|
|
local ck = ngx.var.http_cookie
|
|
|
|
if CookieDeny and ck then
|
|
|
|
for _,rule in pairs(cookieBlockList) do
|
|
|
|
if rule ~="" and ngxmatch(ck,rule,"isjo") then
|
|
|
|
log('Cookie',ngx.var.request_uri,"-",rule)
|
|
|
|
say_html()
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function denycc()
|
2023-02-20 16:31:16 +08:00
|
|
|
if CCDeny and ccRate then
|
|
|
|
local uri=ngx.var.uri
|
|
|
|
CCcount=tonumber(string.match(ccRate,'(.*)/'))
|
|
|
|
CCseconds=tonumber(string.match(ccRate,'/(.*)'))
|
|
|
|
local uri = getClientIp()..uri
|
|
|
|
local limit = ngx.shared.limit
|
|
|
|
local req,_=limit:get(uri)
|
|
|
|
if req then
|
|
|
|
if req > CCcount then
|
|
|
|
ngx.exit(503)
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
limit:incr(token,1)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
limit:set(uri,1,CCseconds)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function get_boundary()
|
2023-02-20 16:31:16 +08:00
|
|
|
local header = get_headers()["content-type"]
|
|
|
|
if not header then
|
|
|
|
return nil
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
|
2023-02-20 16:31:16 +08:00
|
|
|
if type(header) == "table" then
|
|
|
|
header = header[1]
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
|
2023-02-20 16:31:16 +08:00
|
|
|
local m = match(header, ";%s*boundary=\"([^\"]+)\"")
|
|
|
|
if m then
|
|
|
|
return m
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
|
2023-02-20 16:31:16 +08:00
|
|
|
return match(header, ";%s*boundary=([^\",;]+)")
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function whiteip()
|
2023-02-20 16:31:16 +08:00
|
|
|
if IpWhiteAllow then
|
|
|
|
if next(ipWhiteList) ~= nil then
|
|
|
|
for _,ip in pairs(ipWhiteList) do
|
|
|
|
if getClientIp()==ip then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function blockip()
|
2023-02-20 16:31:16 +08:00
|
|
|
if IpBlockDeny then
|
|
|
|
if next(ipBlockList) ~= nil then
|
|
|
|
for _,ip in pairs(ipBlockList) do
|
|
|
|
if getClientIp()==ip then
|
|
|
|
ngx.exit(403)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if whiteip() then
|
|
|
|
elseif blockip() then
|
|
|
|
elseif denycc() then
|
|
|
|
elseif ngx.var.http_Acunetix_Aspect then
|
|
|
|
ngx.exit(444)
|
|
|
|
elseif ngx.var.http_X_Scan_Memo then
|
|
|
|
ngx.exit(444)
|
|
|
|
elseif whiteurl() then
|
|
|
|
elseif ua() then
|
|
|
|
elseif url() then
|
|
|
|
elseif args() then
|
|
|
|
elseif cookie() then
|
|
|
|
elseif PostDeny then
|
2023-02-20 16:31:16 +08:00
|
|
|
if method=="POST" then
|
|
|
|
local boundary = get_boundary()
|
|
|
|
if boundary then
|
|
|
|
local len = string.len
|
2023-02-06 15:53:30 +08:00
|
|
|
local sock, err = ngx.req.socket()
|
2023-02-20 16:31:16 +08:00
|
|
|
if not sock then
|
|
|
|
return
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
2023-02-20 16:31:16 +08:00
|
|
|
ngx.req.init_body(128 * 1024)
|
2023-02-06 15:53:30 +08:00
|
|
|
sock:settimeout(0)
|
2023-02-20 16:31:16 +08:00
|
|
|
local content_length = nil
|
|
|
|
content_length=tonumber(ngx.req.get_headers()['content-length'])
|
|
|
|
local chunk_size = 4096
|
2023-02-06 15:53:30 +08:00
|
|
|
if content_length < chunk_size then
|
2023-02-20 16:31:16 +08:00
|
|
|
chunk_size = content_length
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
local size = 0
|
2023-02-20 16:31:16 +08:00
|
|
|
while size < content_length do
|
|
|
|
local data, err, partial = sock:receive(chunk_size)
|
|
|
|
data = data or partial
|
|
|
|
if not data then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
ngx.req.append_body(data)
|
|
|
|
if body(data) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
size = size + len(data)
|
|
|
|
local m = ngxmatch(data,[[Content-Disposition: form-data;(.+)filename="(.+)\\.(.*)"]],'ijo')
|
|
|
|
if m then
|
|
|
|
fileExtCheck(m[3])
|
|
|
|
filetranslate = true
|
|
|
|
else
|
|
|
|
if ngxmatch(data,"Content-Disposition:",'isjo') then
|
|
|
|
filetranslate = false
|
|
|
|
end
|
|
|
|
if filetranslate==false then
|
|
|
|
if body(data) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local less = content_length - size
|
|
|
|
if less < chunk_size then
|
|
|
|
chunk_size = less
|
|
|
|
end
|
|
|
|
end
|
|
|
|
ngx.req.finish_body()
|
|
|
|
else
|
|
|
|
ngx.req.read_body()
|
|
|
|
local args = ngx.req.get_post_args()
|
|
|
|
if not args then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
for key, val in pairs(args) do
|
|
|
|
if type(val) == "table" then
|
|
|
|
if type(val[1]) == "boolean" then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
data=table.concat(val, ", ")
|
|
|
|
else
|
|
|
|
data=val
|
|
|
|
end
|
|
|
|
if data and type(data) ~= "boolean" and body(data) then
|
|
|
|
body(key)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2023-02-06 15:53:30 +08:00
|
|
|
end
|
|
|
|
else
|
|
|
|
return
|
|
|
|
end
|