-
Notifications
You must be signed in to change notification settings - Fork 419
/
access_all.lua
581 lines (541 loc) · 19.7 KB
/
access_all.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
----- access_all by zj -----
local optl = require("optl")
local stool = require("stool")
local parser = require "resty.bodyparser"
local ngx_var = ngx.var
local ngx_ctx = ngx.ctx
local ngx_unescape_uri = ngx.unescape_uri
local config = optl.config
local config_base = config.base or {}
local action_tag = ""
local string_find = string.find
local string_upper = string.upper
local type = type
local tostring = tostring
local table_insert = table.insert
local ipairs = ipairs
local pairs = pairs
local loadstring = loadstring
local tonumber = tonumber
local math_min = math.min
local limit_ip_dict = ngx.shared.limit_ip_dict
local ip_dict = ngx.shared.ip_dict
local host_dict = ngx.shared.host_dict
-- 经测试可直接用request_id,无重复产生 openresty >= 1.11.0.0
local next_ctx = {request_guid = ngx_var.request_id}
ngx_ctx.next_ctx = next_ctx
-- 获取所有参数的内容
local remoteIp = ngx_var.remote_addr
local host = ngx_unescape_uri(ngx_var.http_host)
local ip = remoteIp
-- local method = ngx_var.request_method
local method = ngx.req.get_method()
local request_uri = ngx_unescape_uri(ngx_var.request_uri)
local uri = ngx_unescape_uri(ngx_var.uri)
local useragent = ngx_unescape_uri(ngx_var.http_user_agent)
local referer = ngx_unescape_uri(ngx_var.http_referer)
local cookie = ngx_unescape_uri(ngx_var.http_cookie)
local query_string = ngx_unescape_uri(ngx_var.query_string)
local headers = ngx.req.get_headers()
local headers_data
-- local function tmp_pcall()
-- headers_data = ngx_unescape_uri(ngx.req.raw_header(false))
-- end
-- pcall(tmp_pcall)
if ngx_var.server_protocol ~= "HTTP/2.0" then
headers_data = ngx_unescape_uri(ngx.req.raw_header(false))
end
local http_content_type = ngx_unescape_uri(ngx_var.http_content_type)
local args = ngx.req.get_uri_args()
local args_data = optl.get_table(args)
local posts = {}
local posts_data = ""
local posts_all
if method == "POST" and http_content_type then
-- multipart/form-data; boundary=----WebKitForm...
local from,to = string_find(http_content_type,"x-www-form-urlencoded",1,true)
if from then
posts = ngx.req.get_post_args() or posts
posts_data = optl.get_table(posts)
end
end
local base_msg = {}
-- string 类型http参数
base_msg.remoteIp = remoteIp
base_msg.host = host
base_msg.ip = ip
base_msg.method = method
base_msg.request_uri = request_uri
base_msg.uri = uri
base_msg.useragent = useragent
base_msg.referer = referer
base_msg.cookie = cookie
base_msg.query_string = query_string
-- table 类型参数
base_msg.headers = headers
base_msg.args = args
base_msg.posts = posts
-- table_str
base_msg.headers_data = headers_data
base_msg.args_data = args_data
base_msg.posts_data = posts_data
next_ctx.base_msg = base_msg
local host_Mod_state = host_dict:get(host)
--- 2016年8月4日 增加全局Mod开关
-- 增加基于host的过滤模块开关判断
if config_base.Mod_state == "off" or host_Mod_state == "off" then
return
end
--- 判断config_dict中模块开关是否开启
local function config_is_on(_config_arg)
if _config_arg == "args_Mod" then
return config_base[_config_arg]["state"] == "on"
elseif _config_arg == "post_Mod" then
return config_base[_config_arg]["state"] == "on"
else
return config_base[_config_arg] == "on"
end
end
--- 取config_dict中的json数据
local function getDict_Config(_Config_jsonName)
local re = config[_Config_jsonName] or {}
return re
-- return (config[_Config_jsonName] or {})
end
--- remath_ext 是 remath_Invert(str,re_str,options,true) 的扩展
local function remath_ext(str,_modRule)
if type(_modRule) ~= "table" then return false end
return optl.remath_Invert(str,_modRule[1],_modRule[2],_modRule[3])
end
--- 访问频率检查 并且计数
-- _tb_network 频率规则 _uid 唯一标识
-- true:触发 频率限制 false:未触发 计数++
local function network_ck(_tb_network,_uid)
if type(_tb_network) ~= "table" then return end
local pTime = _tb_network.pTime or 10
local maxReqs = _tb_network.maxReqs or 50
local ip_count = limit_ip_dict:get(_uid)
if ip_count == nil then
limit_ip_dict:set(_uid,1,pTime)
return
else
if ip_count >= maxReqs then
limit_ip_dict:delete(_uid)
return true
else
limit_ip_dict:incr(_uid,1)
return
end
end
end
--- 匹配 host 和 uri
local function host_uri_remath(_host,_uri)
if remath_ext(host,_host) and remath_ext(uri,_uri) then
return true
end
end
--- 拦截计数 2016年6月7日 21:52:52 up 从全局变成local
local set_count_dict = optl.set_count_dict
-- 执行拦截(deny)操作
local function action_deny()
action_tag = "deny"
-- 2016年9月19日
-- 增加Mod_state = log , host_Mod state = log
-- 在拒绝请求都进行了log记录,仅ip黑名单的没有记录(因为量的问题),故可直接return
if config_base.Mod_state == "log" or host_Mod_state == "log" then
return
end
local denyhtml = tostring(config_base.denyMsg.msg)
local denycode = config_base.denyMsg.http_code or 403
if config_base.denyMsg.state == "on" then
local tb = getDict_Config("denyMsg")
local host_deny_msg = tb[host] or {}
next_ctx.http_code = host_deny_msg.http_code or denycode
denyhtml = host_deny_msg.deny_msg or denyhtml
else
next_ctx.http_code = denycode
end
--ngx.header.content_type = "text/html"
ngx.say(denyhtml)
ngx.exit(200)
end
-- action = {allow,deny,log}
-- 执行 动作时 的 log/计数(拦截操作) 操作
local function do_action(_action,_mod_name,_id,_obj)
_id = _id or tostring(_id)
if _action == "allow" then
return true
elseif _action == "deny" then
set_count_dict(_mod_name.." deny count")
next_ctx.waf_log = next_ctx.waf_log or "[".._mod_name.."] deny No: ".._id
action_deny()
elseif _action == "log" then
set_count_dict(_mod_name.." log count")
next_ctx.waf_log = next_ctx.waf_log or "[".._mod_name.."] log No: ".._id
end
return false
end
-- 获取post_form表单数据
local function get_post_form(_len)
if _len <= 0 then _len = nil end
if posts_all == nil then
posts_all = optl.get_post_all()
base_msg.posts_all = posts_all
end
local p, err = parser.new(posts_all, http_content_type,_len)
if p then
local tmp_tb = {}
while true do
local part_body, name, mime, filename = p:parse_part()
if not part_body then
break
end
table_insert(tmp_tb, {name,filename,mime,part_body})
end
base_msg.post_form = tmp_tb
end
end
--- SETP 0
-- 获取用户真实IP(如有CDN情况下,从header头取)
if config_is_on("realIpFrom_Mod") then
ip = optl.loc_getRealIp(remoteIp,getDict_Config("realIpFrom_Mod")[host])
base_msg.ip = ip
end
--- STEP 1
-- black/white ip 访问控制(黑/白名单/log记录) [ip类拦截数据太多未写日志,可自行取消next_ctx.waf_log赋值的注释]
-- 2016年7月29日19:12:53 检查
if config_is_on("ip_Mod") then
local _ip_v = ip_dict:get(ip) --- 全局IP 黑白名单
if _ip_v == nil then
-- nothing
elseif _ip_v == "allow" then -- 跳出后续规则
return
elseif _ip_v == "log" then
set_count_dict(ip.." log count")
--next_ctx.waf_log = "[ip_Mod] log"
else
--next_ctx.waf_log = "[ip_Mod] deny"
set_count_dict(ip)
action_deny()
end
-- 基于host的ip黑白名单 eg:www.abc.com_101.111.112.113
local tmp_host_ip = host.."_"..ip
local host_ip = ip_dict:get(tmp_host_ip)
if host_ip == nil then
-- nothing
elseif host_ip == "allow" then -- 跳出后续规则
return
elseif host_ip == "log" then
set_count_dict(tmp_host_ip.." log count")
--next_ctx.waf_log = "[host_ip_Mod] log"
else
--next_ctx.waf_log = "[host_ip_Mod] deny"
set_count_dict(tmp_host_ip)
action_deny()
end
end
--- STEP 2
-- host and method 仅允许 访问控制
if config_is_on("host_method_Mod") and action_tag == "" then
local tb_mod = getDict_Config("host_method_Mod")
local check = "deny"
for _,v in ipairs(tb_mod) do
if v.state == "on" and remath_ext(host,v.hostname) and remath_ext(method,v.method) then
check = "next"
break
end
end
if check == "deny" then
set_count_dict("host_method_Mod deny count")
next_ctx.waf_log = next_ctx.waf_log or "[host_method_Mod] deny"
action_deny()
end
end
--- STEP 3
-- rewrite 跳转阶段(set_cookie)
-- 本来想着放到rewrite阶段使用的,方便统一都放到access阶段了。
if config_is_on("rewrite_Mod") and action_tag == "" then
local tb_mod = getDict_Config("rewrite_Mod")
for _,v in ipairs(tb_mod) do
if v.state == "on" and host_uri_remath(v.hostname,v.uri) then
local token_name = v.set_cookie[2] or "token" -- 默认去 cookie_token 这里值
local re_uri = request_uri
if type(v.set_cookie[3]) == "string" and v.set_cookie[3] ~= "" then
re_uri = v.set_cookie[3]
end
if token_name == "" or v.set_cookie[1] == "" then
if method == "POST" then
return ngx.redirect(re_uri , 307)
else
return ngx.redirect(re_uri)
end
end
local token = ngx.md5(v.set_cookie[1] .. ip)
-- 没有设置 tokenname 默认就是 token
if ngx_var["cookie_"..token_name] ~= token then
ngx.header["Set-Cookie"] = {token_name.."=" .. token}
if method == "POST" then
return ngx.redirect(re_uri,307)
else
return ngx.redirect(re_uri)
end
end
end
end
end
--- STEP 4
-- host_Mod 规则过滤
-- 动作支持 (allow deny log)
if host_Mod_state == "on" and action_tag == "" then
-- 后续 可能缓存起来,不用每次都进行 序列化操作
local tb = stool.stringTojson(host_dict:get(host.."_HostMod"))
for i,v in ipairs(tb) do
if v.state == "on" and remath_ext(uri,v.uri) then
if v.app_ext then
if type(v.post_form) == "number" and method == "POST" and base_msg.post_form == nil then
local post_form_n = v.post_form
local base_post_from_n = tonumber(config_base.post_form) or 0
post_form_n = math_min(post_form_n,base_post_from_n)
get_post_form(post_form_n)
end
if optl.re_app_ext(v.app_ext,base_msg) then
if do_action(v.action,"host_Mod",i) then
return
else
break
end
end
elseif v.network then
local blacktime = v.network["blacktime"] or 600
-- ip 计数
local mod_host_ip = host.."_"..ip.." host_network No "..i
if network_ck(v.network,mod_host_ip) then
ip_dict:safe_set(host.."_"..ip,mod_host_ip,blacktime)
next_ctx.waf_log = next_ctx.waf_log or "[host_Mod] deny No: "..i
-- network 触发直接拦截
set_count_dict(host.." deny count")
action_deny()
break
end
end
end
end
end
-- --- STEP 5
-- -- app_Mod 访问控制 (自定义action)
-- -- 目前支持的 deny allow log rehtml refile relua relua_str
-- 支持 规则组 取反 or/and 连接符
if config_is_on("app_Mod") and action_tag == "" then
local app_mod = getDict_Config("app_Mod")
for i,v in ipairs(app_mod) do
if v.state == "on" and host_uri_remath(v.hostname,v.uri) then
if type(v.post_form) == "number" and method == "POST" and base_msg.post_form == nil then
local post_form_n = v.post_form
local base_post_from_n = tonumber(config_base.post_form) or 0
post_form_n = math_min(post_form_n,base_post_from_n)
get_post_form(post_form_n)
end
if v.app_ext == nil or optl.re_app_ext(v.app_ext,base_msg) then
if v.action[1] == "deny" then
set_count_dict("app deny count")
next_ctx.waf_log = next_ctx.waf_log or "[app_Mod] deny No: "..i
action_deny()
break
elseif v.action[1] == "allow" then
-- 直接返回
return
elseif v.action[1] == "log" then
if method == "POST" and posts_all == nil then
posts_all = optl.get_post_all()
base_msg.posts_all = posts_all
end
stool.writefile(config_base.logPath.."app.log","log Msg : \n"..stool.tableTojsonStr(base_msg))
-- app_Mod的action=log单独记录,用于debug调试
elseif v.action[1] == "rehtml" then
optl.sayHtml_ext(v.rehtml,true)
break
elseif v.action[1] == "refile" then
optl.sayFile(config_base.htmlPath..v.refile[1],v.refile[2])
break
-- 2016年10月27日 新增 动态执行lua字符串
elseif v.action[1] == "relua_str" then
local re_lua_do = loadstring(v.relua_str)
if re_lua_do() == "break" then
ngx.exit(200)
end
elseif v.action[1] == "relua" then
local re_saylua = optl.sayLua(config_base.htmlPath..v.relua)
if re_saylua == "break" then
ngx.exit(200)
end
end
end
end
end
end
-- --- STEP 6
-- -- referer过滤模块
-- 动作支持(allow deny log)
if config_is_on("referer_Mod") and action_tag == "" then
local ref_mod = getDict_Config("referer_Mod")
for i, v in ipairs( ref_mod ) do
if v.state == "on" and host_uri_remath(v.hostname,v.uri)
and remath_ext(referer,v.referer) then
local _action = v.action or "deny"
if do_action(_action,"uri_Mod",i) then
return
else
break
end
end
end
end
--- STEP 7
-- uri 过滤(黑/白名单/log)
if config_is_on("uri_Mod") and action_tag == "" then
local uri_mod = getDict_Config("uri_Mod")
for i, v in ipairs( uri_mod ) do
if v.state == "on" and host_uri_remath(v.hostname,v.uri) then
local _action = v.action or "deny"
if do_action(_action,"uri_Mod",i) then
return
else
break
end
end
end
end
--- STEP 8
-- header 过滤(黑名单) [scanner]
if config_is_on("header_Mod") and action_tag == "" then
local tb_mod = getDict_Config("header_Mod")
for i,v in ipairs(tb_mod) do
if v.state == "on" and host_uri_remath(v.hostname,v.uri) then
if optl.action_remath("headers",v.header,base_msg) then
set_count_dict("header deny count")
next_ctx.waf_log = next_ctx.waf_log or "[header_Mod] deny No: "..i
action_deny()
break
end
end
end
end
--- STEP 9
-- useragent(黑、白名单/log记录)
if config_is_on("useragent_Mod") and action_tag == "" then
local uagent_mod = getDict_Config("useragent_Mod")
for i, v in ipairs( uagent_mod ) do
if v.state == "on" and remath_ext(host,v.hostname) and remath_ext(useragent,v.useragent) then
local _action = v.action or "deny"
if do_action(_action,"useragent_Mod",i) then
return
else
break
end
end
end
end
--- STEP 10
-- cookie (黑/白名单/log记录)
if config_is_on("cookie_Mod") and action_tag == "" then
local cookie_mod = getDict_Config("cookie_Mod")
for i, v in ipairs( cookie_mod ) do
if v.state == "on" and remath_ext(host,v.hostname) and remath_ext(cookie,v.cookie) then
local _action = v.action or "deny"
if do_action(_action,"cookie_Mod",i) then
return
else
break
end
end
end
end
--- STEP 11
-- args [args_data] (黑/白名单/log记录)
if config_is_on("args_Mod") and action_tag == "" then
if config_base.args_Mod["HPP_state"] == "on" then
for k,v in pairs(args) do
if type(v) == "table" then
do_action("deny","args_HPP","0")
break
end
end
end
if action_tag == "" then
local args_mod = getDict_Config("args_Mod")
for i,v in ipairs(args_mod) do
if v.state == "on" and remath_ext(host,v.hostname) and remath_ext(args_data,v.args_data) then
local _action = v.action or "deny"
if do_action(_action,"args_Mod",i) then
return
else
break
end
end
end
end
end
--- STEP 12
-- post (黑/白名单)
if config_is_on("post_Mod") and action_tag == "" and method == "POST" then
if config_base.post_Mod["HPP_state"] == "on" then
for k,v in pairs(posts) do
if type(v) == "table" then
do_action("deny","posts_HPP","0")
break
end
end
end
if action_tag == "" then
local post_mod = getDict_Config("post_Mod")
for i,v in ipairs(post_mod) do
if v.state == "on" and remath_ext(host,v.hostname) and remath_ext(posts_data,v.posts_data) then
local _action = v.action or "deny"
if do_action(_action,"post_Mod",i) then
return
else
break
end
end
end
end
end
--- STEP 13
-- network_Mod 访问控制
if config_is_on("network_Mod") and action_tag == "" then
local tb_networkMod = getDict_Config("network_Mod")
for i, v in ipairs( tb_networkMod ) do
if v.state =="on" and host_uri_remath(v.hostname,v.uri) then
local blacktime = v.network.blackTime or 10*60
-- ip 属性 计数 (v.id 也可作为唯一标识)
local deny_guid = ip.." network_Mod No "..i
if network_ck(v.network,deny_guid) then
if v.hostname[2] == "" then
if v.hostname[1] == "*" then
ip_dict:safe_set(ip,deny_guid,blacktime)
else
ip_dict:safe_set(host.."_"..ip,deny_guid,blacktime)
end
else
ip_dict:safe_set(host.."_"..ip,deny_guid,blacktime)
end
next_ctx.waf_log = next_ctx.waf_log or "[network_Mod] deny No: "..i
action_deny()
--ngx.say("frist network deny")
break
end
end
end
end
--- STEP 14
if config_is_on("replace_Mod") and action_tag == "" then
local Replace_Mod = getDict_Config("replace_Mod")
for _,v in ipairs(Replace_Mod) do
if v.state =="on" and host_uri_remath(v.hostname,v.uri) then
ngx.req.clear_header("Accept-Encoding")-- 取消浏览器要求gzip操作)
next_ctx.replace_Mod = v
--ngx_ctx.body_mod = v
break
end
end
end