mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-27 12:39:01 +08:00
feat(waf): 增加内部接口请求安全性 (#4224)
This commit is contained in:
parent
94028d97cf
commit
d7d7c7fd73
94
plugins/openresty/waf/conf/siteConfig.json
Normal file
94
plugins/openresty/waf/conf/siteConfig.json
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"waf": {
|
||||||
|
"state": "on",
|
||||||
|
"mode": "protection",
|
||||||
|
"secret": "qwer1234"
|
||||||
|
},
|
||||||
|
"args": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "args",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
},
|
||||||
|
"defaultUaBlack": {
|
||||||
|
"type": "defaultUaBlack",
|
||||||
|
"state": "on",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "cookie",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
},
|
||||||
|
"bot": {
|
||||||
|
"type": "bot",
|
||||||
|
"state": "on",
|
||||||
|
"uri": "/1pwaf/bot/trap",
|
||||||
|
"action": "deny",
|
||||||
|
"ipBlock": "off",
|
||||||
|
"ipBlockTime": 600
|
||||||
|
},
|
||||||
|
"geoRestrict": {
|
||||||
|
"state": "off",
|
||||||
|
"rules": [
|
||||||
|
"CN"
|
||||||
|
],
|
||||||
|
"action": "allow"
|
||||||
|
},
|
||||||
|
"defaultIpBlack": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "defaultIpBlack",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
},
|
||||||
|
"xss": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "xss",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
},
|
||||||
|
"sql": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "sql",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
},
|
||||||
|
"cc": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "cc",
|
||||||
|
"rule": "cc",
|
||||||
|
"tokenTimeOut": 1800,
|
||||||
|
"threshold": 300,
|
||||||
|
"duration": 60,
|
||||||
|
"action": "deny",
|
||||||
|
"ipBlock": "on",
|
||||||
|
"ipBlockTime": 600
|
||||||
|
},
|
||||||
|
"ccurl": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "ccurl",
|
||||||
|
"action": "deny",
|
||||||
|
"ipBlock": "off",
|
||||||
|
"ipBlockTime": 600
|
||||||
|
},
|
||||||
|
"fileExt": {
|
||||||
|
"state": "on",
|
||||||
|
"action": "deny",
|
||||||
|
"code": 403,
|
||||||
|
"type": "fileExtCheck"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"state": "on",
|
||||||
|
"type": "header",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
},
|
||||||
|
"defaultUrlBlack": {
|
||||||
|
"type": "defaultUrlBlack",
|
||||||
|
"state": "on",
|
||||||
|
"code": 403,
|
||||||
|
"action": "deny"
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
local file_utils = require "file"
|
local file_utils = require "file"
|
||||||
local lfs = require "lfs"
|
local lfs = require "lfs"
|
||||||
local cjson = require "cjson"
|
local utils = require "utils"
|
||||||
|
|
||||||
local read_rule = file_utils.read_rule
|
local read_rule = file_utils.read_rule
|
||||||
local read_file2string = file_utils.read_file2string
|
local read_file2string = file_utils.read_file2string
|
||||||
local read_file2table = file_utils.read_file2table
|
local read_file2table = file_utils.read_file2table
|
||||||
|
local set_content_to_json_file = file_utils.set_content_to_json_file
|
||||||
local list_dir = lfs.dir
|
local list_dir = lfs.dir
|
||||||
local attributes = lfs.attributes
|
local attributes = lfs.attributes
|
||||||
local match_str = string.match
|
local match_str = string.match
|
||||||
@ -16,6 +17,7 @@ local site_dir = waf_dir .. 'sites/'
|
|||||||
|
|
||||||
local _M = {}
|
local _M = {}
|
||||||
local config = {}
|
local config = {}
|
||||||
|
local global_config = {}
|
||||||
|
|
||||||
local function init_sites_config()
|
local function init_sites_config()
|
||||||
local site_config = {}
|
local site_config = {}
|
||||||
@ -55,9 +57,7 @@ local function init_sites_config()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ngx.log(ngx.NOTICE, "Load config" .. cjson.encode(site_config))
|
|
||||||
config.site_config = site_config
|
config.site_config = site_config
|
||||||
ngx.log(ngx.NOTICE, "Load rules" .. cjson.encode(site_rules))
|
|
||||||
config.site_rules = site_rules
|
config.site_rules = site_rules
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -69,9 +69,19 @@ local function ini_waf_info()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function init_global_config()
|
local function init_global_config()
|
||||||
local global_config = read_file2table(config_dir .. 'global.json')
|
local global_config_file = config_dir .. 'global.json'
|
||||||
|
global_config = file_utils.read_file2table(global_config_file)
|
||||||
|
local token = utils.random_string(20)
|
||||||
|
global_config["waf"]["token"] = token
|
||||||
|
|
||||||
|
local waf_dict = ngx.shared.waf
|
||||||
|
waf_dict:set("token", token, 7200)
|
||||||
|
|
||||||
|
set_content_to_json_file(global_config,global_config_file)
|
||||||
config.global_config = global_config
|
config.global_config = global_config
|
||||||
|
|
||||||
config.isProtectionMode = global_config["mode"] == "protection" and true or false
|
config.isProtectionMode = global_config["mode"] == "protection" and true or false
|
||||||
|
|
||||||
|
|
||||||
local rules = {}
|
local rules = {}
|
||||||
rules.uaBlack = read_rule(global_rule_dir, "uaBlack")
|
rules.uaBlack = read_rule(global_rule_dir, "uaBlack")
|
||||||
@ -156,4 +166,17 @@ function _M.get_secret()
|
|||||||
return config.global_config["waf"]["secret"]
|
return config.global_config["waf"]["secret"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function _M.get_token()
|
||||||
|
local waf_dict = ngx.shared.waf
|
||||||
|
local token = waf_dict:get("token")
|
||||||
|
if not token then
|
||||||
|
token = utils.random_string(20)
|
||||||
|
waf_dict:set("token", token, 86400)
|
||||||
|
global_config["waf"]["token"] = token
|
||||||
|
local global_config_file = config_dir .. 'global.json'
|
||||||
|
set_content_to_json_file(global_config,global_config_file)
|
||||||
|
end
|
||||||
|
return token
|
||||||
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
@ -5,6 +5,9 @@ local format_str = string.format
|
|||||||
local _M = {}
|
local _M = {}
|
||||||
|
|
||||||
local function deny(status_code, res)
|
local function deny(status_code, res)
|
||||||
|
if not status_code then
|
||||||
|
status_code = 403
|
||||||
|
end
|
||||||
ngx.status = status_code
|
ngx.status = status_code
|
||||||
if res then
|
if res then
|
||||||
ngx.header.content_type = "text/html; charset=UTF-8"
|
ngx.header.content_type = "text/html; charset=UTF-8"
|
||||||
@ -38,11 +41,11 @@ end
|
|||||||
|
|
||||||
function _M.block_ip(ip, rule)
|
function _M.block_ip(ip, rule)
|
||||||
local ok, err = nil, nil
|
local ok, err = nil, nil
|
||||||
local msg = "拉黑IP : " .. ip .. "国家 " .. ngx.ctx.ip_location.country["zh"]
|
--local msg = "拉黑IP : " .. ip .. "国家 " .. ngx.ctx.ip_location.country["zh"]
|
||||||
if rule then
|
--if rule then
|
||||||
msg = msg .. " 规则 " .. rule.type
|
-- msg = msg .. " 规则 " .. rule.type
|
||||||
end
|
--end
|
||||||
ngx.log(ngx.ERR, msg)
|
--ngx.log(ngx.ERR, msg)
|
||||||
|
|
||||||
if config.redis_on then
|
if config.redis_on then
|
||||||
local red, err1 = redis_util.get_conn()
|
local red, err1 = redis_util.get_conn()
|
||||||
@ -140,26 +143,22 @@ function _M.exec_action(rule_config, match_rule, data)
|
|||||||
|
|
||||||
attack_count(rule_config.type)
|
attack_count(rule_config.type)
|
||||||
|
|
||||||
local msg = "访问 IP " .. ngx.ctx.ip .. " 访问 URL" .. ngx.var.uri .. " 触发动作 " .. action .. " 规则类型 " .. rule_config.type
|
--local msg = "访问 IP " .. ngx.ctx.ip .. " 访问 URL" .. ngx.var.uri .. " 触发动作 " .. action .. " 规则类型 " .. rule_config.type
|
||||||
if match_rule then
|
--if match_rule then
|
||||||
if match_rule.type then
|
-- if match_rule.type then
|
||||||
msg = msg .. " 触发规则类型 " .. match_rule.type
|
-- msg = msg .. " 触发规则类型 " .. match_rule.type
|
||||||
else
|
-- else
|
||||||
msg = msg .. " 触发规则 " .. match_rule.rule
|
-- msg = msg .. " 触发规则 " .. match_rule.rule
|
||||||
end
|
-- end
|
||||||
end
|
--end
|
||||||
|
--
|
||||||
ngx.log(ngx.ERR, msg)
|
--ngx.log(ngx.ERR, msg)
|
||||||
if action == "allow" then
|
if action == "allow" then
|
||||||
return
|
return
|
||||||
|
|
||||||
elseif action == "deny" then
|
elseif action == "deny" then
|
||||||
if rule_config.code and rule_config.code ~= 444 then
|
deny(rule_config.code, rule_config.res)
|
||||||
deny(rule_config.code, rule_config.res)
|
|
||||||
else
|
|
||||||
deny(444)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif action == "slide" then
|
elseif action == "slide" then
|
||||||
slide()
|
slide()
|
||||||
|
|
||||||
@ -167,7 +166,7 @@ function _M.exec_action(rule_config, match_rule, data)
|
|||||||
five_second()
|
five_second()
|
||||||
|
|
||||||
else
|
else
|
||||||
redirect(444)
|
redirect(403)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -59,6 +59,16 @@ function _M.read_file2table(file_path)
|
|||||||
return decode(str)
|
return decode(str)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function _M.set_content_to_json_file(data, file_path)
|
||||||
|
local json_str = cjson.encode(data)
|
||||||
|
local file = open_file(file_path, "w")
|
||||||
|
if file then
|
||||||
|
file:write(json_str)
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function _M.read_file2string(file_path, binary)
|
function _M.read_file2string(file_path, binary)
|
||||||
if not file_path then
|
if not file_path then
|
||||||
ngx.log(ngx.ERR, "No file found ", file_path)
|
ngx.log(ngx.ERR, "No file found ", file_path)
|
||||||
|
@ -143,7 +143,6 @@ end
|
|||||||
local function xss_and_sql_check(kv)
|
local function xss_and_sql_check(kv)
|
||||||
if type(kv) ~= 'string' then
|
if type(kv) ~= 'string' then
|
||||||
return
|
return
|
||||||
|
|
||||||
end
|
end
|
||||||
if is_site_state_on("xss") then
|
if is_site_state_on("xss") then
|
||||||
local is_xss, fingerprint = libinjection.xss(tostring(kv))
|
local is_xss, fingerprint = libinjection.xss(tostring(kv))
|
||||||
@ -161,7 +160,6 @@ local function xss_and_sql_check(kv)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -464,12 +462,13 @@ function _M.args_check()
|
|||||||
val_arr = concat_table(val, ", ")
|
val_arr = concat_table(val, ", ")
|
||||||
end
|
end
|
||||||
if val_arr and type(val_arr) ~= "boolean" and val_arr ~= "" then
|
if val_arr and type(val_arr) ~= "boolean" and val_arr ~= "" then
|
||||||
local m, mr = match_rule(args_list, utils.unescape_uri(val_arr))
|
local check_value = utils.unescape_uri(val_arr)
|
||||||
|
xss_and_sql_check(check_value)
|
||||||
|
local m, mr = match_rule(args_list,check_value)
|
||||||
if m then
|
if m then
|
||||||
exec_action(get_global_config("args"), mr)
|
exec_action(get_global_config("args"), mr)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
xss_and_sql_check(val_arr)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -495,21 +494,19 @@ function _M.header_check()
|
|||||||
local headers_config = get_site_config("header")
|
local headers_config = get_site_config("header")
|
||||||
local referer = ngx.var.http_referer
|
local referer = ngx.var.http_referer
|
||||||
if referer and referer ~= "" then
|
if referer and referer ~= "" then
|
||||||
local m = match_rule(headers_rule, referer)
|
local check_value = utils.unescape_uri(referer)
|
||||||
|
local m = match_rule(headers_rule, check_value)
|
||||||
if m then
|
if m then
|
||||||
exec_action(headers_config)
|
exec_action(headers_config)
|
||||||
end
|
end
|
||||||
|
xss_and_sql_check(check_value)
|
||||||
end
|
end
|
||||||
local headers = utils.get_headers()
|
local headers = utils.get_headers()
|
||||||
if headers then
|
if headers then
|
||||||
for k, v in pairs(headers) do
|
for _, v in pairs(headers) do
|
||||||
local m1, mr1 = match_rule(headers_rule, k)
|
local m, mr = match_rule(headers_rule, v)
|
||||||
if m1 then
|
if m then
|
||||||
exec_action(headers_config, mr1)
|
exec_action(headers_config, mr)
|
||||||
end
|
|
||||||
local m2, mr2 = match_rule(headers_rule, v)
|
|
||||||
if m2 then
|
|
||||||
exec_action(headers_config, mr2)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,7 +8,8 @@ local type = type
|
|||||||
local find_str = string.find
|
local find_str = string.find
|
||||||
local gmatch_str = string.gmatch
|
local gmatch_str = string.gmatch
|
||||||
local pcall = pcall
|
local pcall = pcall
|
||||||
local cjson = require "cjson"
|
local random = math.random
|
||||||
|
local unescape_uri = ngx.unescape_uri
|
||||||
|
|
||||||
local _M = {}
|
local _M = {}
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ end
|
|||||||
function _M.unescape_uri(str)
|
function _M.unescape_uri(str)
|
||||||
local newStr = str
|
local newStr = str
|
||||||
for t = 1, 2 do
|
for t = 1, 2 do
|
||||||
local temp = ngx.unescape_uri(newStr)
|
local temp = unescape_uri(newStr)
|
||||||
if not temp then
|
if not temp then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@ -128,14 +129,14 @@ function _M.get_ip_location(ip)
|
|||||||
else
|
else
|
||||||
geoip.init()
|
geoip.init()
|
||||||
local geo_res = geoip.lookup(ip)
|
local geo_res = geoip.lookup(ip)
|
||||||
local msg = "访问 IP " .. ip
|
--local msg = "访问 IP " .. ip
|
||||||
if geo_res.country then
|
--if geo_res.country then
|
||||||
msg = msg .. " 国家 " .. cjson.encode(geo_res.country)
|
-- msg = msg .. " 国家 " .. cjson.encode(geo_res.country)
|
||||||
end
|
--end
|
||||||
if geo_res.province then
|
--if geo_res.province then
|
||||||
msg = msg .. " 省份 " .. cjson.encode(geo_res.province)
|
-- msg = msg .. " 省份 " .. cjson.encode(geo_res.province)
|
||||||
end
|
--end
|
||||||
ngx.log(ngx.ERR, msg)
|
--ngx.log(ngx.ERR, msg)
|
||||||
return geo_res
|
return geo_res
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -182,4 +183,18 @@ function _M.get_wafdb(waf_db_path)
|
|||||||
end
|
end
|
||||||
return sqlite3.open(waf_db_path)
|
return sqlite3.open(waf_db_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
math.randomseed(os.time())
|
||||||
|
|
||||||
|
function _M.random_string(length)
|
||||||
|
local charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
local str = ""
|
||||||
|
for i = 1, length do
|
||||||
|
local rand_index = random(1, #charset)
|
||||||
|
str = str .. sub_str(charset, rand_index, rand_index)
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
||||||
|
@ -160,10 +160,10 @@ local function write_req_log(attack)
|
|||||||
|
|
||||||
wafdb:execute([[COMMIT]])
|
wafdb:execute([[COMMIT]])
|
||||||
|
|
||||||
local error_msg = wafdb:errmsg()
|
--local error_msg = wafdb:errmsg()
|
||||||
if error_msg then
|
--if error_msg then
|
||||||
ngx.log(ngx.ERR, "insert attack_log error ", error_msg .. " ")
|
-- ngx.log(ngx.ERR, "insert attack_log error ", error_msg .. " ")
|
||||||
end
|
--end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,6 +72,54 @@
|
|||||||
"name": "appFilter4",
|
"name": "appFilter4",
|
||||||
"type": "appFilter"
|
"type": "appFilter"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/password_change.cgi",
|
||||||
|
"name": "appFilter5",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/service/extdirect",
|
||||||
|
"name": "appFilter6",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/api/jsonws/invoke",
|
||||||
|
"name": "appFilter7",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/jars/upload",
|
||||||
|
"name": "appFilter8",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/example/tree/a/search",
|
||||||
|
"name": "appFilter9",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/actuator/gateway/routes/hacktest",
|
||||||
|
"name": "appFilter10",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/api/v1/method.callAnon/getPasswordPolicy",
|
||||||
|
"name": "appFilter11",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "/functionRouter",
|
||||||
|
"name": "appFilter12",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"state": "on",
|
"state": "on",
|
||||||
"rule": "(?:(union(.*?)select))",
|
"rule": "(?:(union(.*?)select))",
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
local geoip = require "geoip"
|
|
||||||
local lib = require "lib"
|
local lib = require "lib"
|
||||||
local file_utils = require "file"
|
local file_utils = require "file"
|
||||||
local config = require "config"
|
local config = require "config"
|
||||||
@ -104,10 +103,28 @@ local function waf_api()
|
|||||||
if uri == "/5s_check_" .. ngx.md5(ngx.ctx.ip) .. ".js" then
|
if uri == "/5s_check_" .. ngx.md5(ngx.ctx.ip) .. ".js" then
|
||||||
return_js("five_second_js")
|
return_js("five_second_js")
|
||||||
end
|
end
|
||||||
|
local method = ngx.req.get_method()
|
||||||
|
if method ~= 'POST' then
|
||||||
|
return false
|
||||||
|
end
|
||||||
if ngx.var.remote_addr ~= '127.0.0.1' then
|
if ngx.var.remote_addr ~= '127.0.0.1' then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
ngx.req.read_body()
|
||||||
|
local body_data = ngx.req.get_body_data()
|
||||||
|
if not body_data then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local args
|
||||||
|
if body_data then
|
||||||
|
args = cjson.decode(body_data)
|
||||||
|
end
|
||||||
|
if args == nil or args.token == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if args.token ~= config.get_token() then
|
||||||
|
return false
|
||||||
|
end
|
||||||
if uri == '/reload_waf_config' then
|
if uri == '/reload_waf_config' then
|
||||||
config.load_config_file()
|
config.load_config_file()
|
||||||
ngx.exit(200)
|
ngx.exit(200)
|
||||||
@ -119,6 +136,7 @@ local function waf_api()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if config.is_waf_on() then
|
if config.is_waf_on() then
|
||||||
init()
|
init()
|
||||||
waf_api()
|
waf_api()
|
||||||
|
Loading…
Reference in New Issue
Block a user