mirror of https://github.com/midoks/mdserver-web
parent
5a52ed41ad
commit
34f8ef702d
@ -0,0 +1,30 @@ |
|||||||
|
|
||||||
|
|
||||||
|
function return_message(status,msg) |
||||||
|
ngx.header.content_type = "application/json;" |
||||||
|
ngx.status = status |
||||||
|
ngx.say(json.encode(msg)) |
||||||
|
ngx.exit(status) |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
function return_html(status,html) |
||||||
|
ngx.header.content_type = "text/html" |
||||||
|
ngx.status = status |
||||||
|
ngx.say(html) |
||||||
|
ngx.exit(status) |
||||||
|
end |
||||||
|
|
||||||
|
function read_file_body(filename) |
||||||
|
fp = io.open(filename,'r') |
||||||
|
if fp == nil then |
||||||
|
return nil |
||||||
|
end |
||||||
|
fbody = fp:read("*a") |
||||||
|
fp:close() |
||||||
|
if fbody == '' then |
||||||
|
return nil |
||||||
|
end |
||||||
|
return fbody |
||||||
|
end |
||||||
|
|
@ -1,261 +1,52 @@ |
|||||||
RulePath = "{$WAF_PATH}/wafconf" |
local cpath = "{$WAF_PATH}/" |
||||||
attacklog = "on" |
local logdir = "{$ROOT_PATH}/wwwlogs/waf/" |
||||||
logdir = "{$ROOT_PATH}/wwwlogs/waf/" |
local json = require "cjson" |
||||||
UrlDeny="on" |
|
||||||
Redirect="on" |
|
||||||
CookieMatch="off" |
|
||||||
postMatch="off" |
|
||||||
whiteModule="on" |
|
||||||
black_fileExt={"php","jsp"} |
|
||||||
ipWhitelist={"127.0.0.2"} |
|
||||||
ipBlocklist={"1.0.0.1"} |
|
||||||
CCDeny="off" |
|
||||||
CCrate="300/60" |
|
||||||
|
|
||||||
|
function return_message(status,msg) |
||||||
|
ngx.header.content_type = "application/json;" |
||||||
require 'config' |
ngx.status = status |
||||||
local match = string.match |
ngx.say(json.encode(msg)) |
||||||
local ngxmatch=ngx.re.find |
ngx.exit(status) |
||||||
local unescape=ngx.unescape_uri |
|
||||||
local get_headers = ngx.req.get_headers |
|
||||||
local optionIsOn = function (options) return options == "on" and true or false end |
|
||||||
logpath = logdir |
|
||||||
rulepath = RulePath |
|
||||||
UrlDeny = optionIsOn(UrlDeny) |
|
||||||
PostCheck = optionIsOn(postMatch) |
|
||||||
CookieCheck = optionIsOn(cookieMatch) |
|
||||||
WhiteCheck = optionIsOn(whiteModule) |
|
||||||
PathInfoFix = optionIsOn(PathInfoFix) |
|
||||||
attacklog = optionIsOn(attacklog) |
|
||||||
CCDeny = optionIsOn(CCDeny) |
|
||||||
Redirect=optionIsOn(Redirect) |
|
||||||
function getClientIp() |
|
||||||
IP = ngx.var.remote_addr |
|
||||||
if IP == nil then |
|
||||||
IP = "unknown" |
|
||||||
end |
|
||||||
return IP |
|
||||||
end |
|
||||||
function write(logfile,msg) |
|
||||||
local fd = io.open(logfile,"ab") |
|
||||||
if fd == nil then return end |
|
||||||
fd:write(msg) |
|
||||||
fd:flush() |
|
||||||
fd:close() |
|
||||||
end |
|
||||||
function log(method,url,data,ruletag) |
|
||||||
if attacklog then |
|
||||||
local realIp = getClientIp() |
|
||||||
local ua = ngx.var.http_user_agent |
|
||||||
local servername=ngx.var.server_name |
|
||||||
local time=ngx.localtime() |
|
||||||
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 |
|
||||||
end |
|
||||||
------------------------------------规则读取函数------------------------------------------------------------------- |
|
||||||
function read_rule(var) |
|
||||||
file = io.open(rulepath..'/'..var,"r") |
|
||||||
if file==nil then |
|
||||||
return |
|
||||||
end |
|
||||||
t = {} |
|
||||||
for line in file:lines() do |
|
||||||
table.insert(t,line) |
|
||||||
end |
|
||||||
file:close() |
|
||||||
return(t) |
|
||||||
end |
end |
||||||
|
|
||||||
urlrules=read_rule('url') |
|
||||||
argsrules=read_rule('args') |
|
||||||
uarules=read_rule('user-agent') |
|
||||||
wturlrules=read_rule('whiteurl') |
|
||||||
postrules=read_rule('post') |
|
||||||
ckrules=read_rule('cookie') |
|
||||||
html=read_rule('returnhtml') |
|
||||||
|
|
||||||
function say_html() |
function return_html(status,html) |
||||||
if Redirect then |
|
||||||
ngx.header.content_type = "text/html" |
ngx.header.content_type = "text/html" |
||||||
ngx.status = ngx.HTTP_FORBIDDEN |
ngx.status = status |
||||||
ngx.say(html) |
ngx.say(html) |
||||||
ngx.exit(ngx.status) |
ngx.exit(status) |
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
function whiteurl() |
|
||||||
if WhiteCheck then |
|
||||||
if wturlrules ~=nil then |
|
||||||
for _,rule in pairs(wturlrules) do |
|
||||||
if ngxmatch(ngx.var.uri,rule,"isjo") then |
|
||||||
return true |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
return false |
|
||||||
end |
|
||||||
function fileExtCheck(ext) |
|
||||||
local items = Set(black_fileExt) |
|
||||||
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 |
|
||||||
return false |
|
||||||
end |
|
||||||
function Set (list) |
|
||||||
local set = {} |
|
||||||
for _, l in ipairs(list) do set[l] = true end |
|
||||||
return set |
|
||||||
end |
|
||||||
function args() |
|
||||||
for _,rule in pairs(argsrules) do |
|
||||||
local args = ngx.req.get_uri_args() |
|
||||||
for key, val in pairs(args) 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 |
|
||||||
return false |
|
||||||
end |
end |
||||||
|
|
||||||
|
function read_file_body(filename) |
||||||
function url() |
fp = io.open(filename,'r') |
||||||
if UrlDeny then |
if fp == nil then |
||||||
for _,rule in pairs(urlrules) do |
return nil |
||||||
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 |
|
||||||
end |
|
||||||
|
|
||||||
function ua() |
|
||||||
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 |
|
||||||
end |
|
||||||
function body(data) |
|
||||||
for _,rule in pairs(postrules) 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 |
|
||||||
end |
|
||||||
function cookie() |
|
||||||
local ck = ngx.var.http_cookie |
|
||||||
if CookieCheck and ck then |
|
||||||
for _,rule in pairs(ckrules) 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 |
|
||||||
end |
|
||||||
|
|
||||||
function denycc() |
|
||||||
if CCDeny then |
|
||||||
local uri=ngx.var.uri |
|
||||||
CCcount=tonumber(string.match(CCrate,'(.*)/')) |
|
||||||
CCseconds=tonumber(string.match(CCrate,'/(.*)')) |
|
||||||
local token = getClientIp()..uri |
|
||||||
local limit = ngx.shared.limit |
|
||||||
local req,_=limit:get(token) |
|
||||||
if req then |
|
||||||
if req > CCcount then |
|
||||||
ngx.exit(444) |
|
||||||
return true |
|
||||||
else |
|
||||||
limit:incr(token,1) |
|
||||||
end |
|
||||||
else |
|
||||||
limit:set(token,1,CCseconds) |
|
||||||
end |
end |
||||||
|
fbody = fp:read("*a") |
||||||
|
fp:close() |
||||||
|
if fbody == '' then |
||||||
|
return nil |
||||||
end |
end |
||||||
return false |
return fbody |
||||||
end |
end |
||||||
|
|
||||||
function get_boundary() |
|
||||||
local header = get_headers()["content-type"] |
|
||||||
if not header then |
|
||||||
return nil |
|
||||||
end |
|
||||||
|
|
||||||
if type(header) == "table" then |
-- local config = read_file_body(cpath .. 'config.json') |
||||||
header = header[1] |
local config = json.decode(read_file_body(cpath .. 'config.json')) |
||||||
end |
-- local site_config = json.decode(read_file_body(cpath .. 'site.json')) |
||||||
|
|
||||||
local m = match(header, ";%s*boundary=\"([^\"]+)\"") |
|
||||||
if m then |
|
||||||
return m |
|
||||||
end |
|
||||||
|
|
||||||
return match(header, ";%s*boundary=([^\",;]+)") |
-- function args() |
||||||
end |
-- if not config['get']['open'] or not is_site_config('get') then return false end |
||||||
|
-- if is_ngx_match(args_rules,uri_request_args,'args') then |
||||||
|
-- write_log('args','regular') |
||||||
|
-- return_html(config['get']['status'],get_html) |
||||||
|
-- return true |
||||||
|
-- end |
||||||
|
-- return false |
||||||
|
-- end |
||||||
|
|
||||||
function whiteip() |
function waf() |
||||||
if next(ipWhitelist) ~= nil then |
-- return_html(200,cpath .. 'config.json') |
||||||
for _,ip in pairs(ipWhitelist) do |
return_message(200, config) |
||||||
if getClientIp()==ip then |
|
||||||
return true |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
return false |
|
||||||
end |
|
||||||
|
|
||||||
function blockip() |
|
||||||
if next(ipBlocklist) ~= nil then |
|
||||||
for _,ip in pairs(ipBlocklist) do |
|
||||||
if getClientIp()==ip then |
|
||||||
ngx.exit(444) |
|
||||||
return true |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
return false |
|
||||||
end |
end |
||||||
|
@ -1,88 +1 @@ |
|||||||
local content_length=tonumber(ngx.req.get_headers()['content-length']) |
waf() |
||||||
local method=ngx.req.get_method() |
|
||||||
local ngxmatch=ngx.re.match |
|
||||||
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 PostCheck then |
|
||||||
if method=="POST" then |
|
||||||
local boundary = get_boundary() |
|
||||||
if boundary then |
|
||||||
local len = string.len |
|
||||||
local sock, err = ngx.req.socket() |
|
||||||
if not sock then |
|
||||||
return |
|
||||||
end |
|
||||||
ngx.req.init_body(128 * 1024) |
|
||||||
sock:settimeout(0) |
|
||||||
local content_length = nil |
|
||||||
content_length=tonumber(ngx.req.get_headers()['content-length']) |
|
||||||
local chunk_size = 4096 |
|
||||||
if content_length < chunk_size then |
|
||||||
chunk_size = content_length |
|
||||||
end |
|
||||||
local size = 0 |
|
||||||
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 |
|
||||||
end |
|
||||||
else |
|
||||||
return |
|
||||||
end |
|
Loading…
Reference in new issue