├── README.MD
├── config.lua
├── init.lua
├── main.lua
└── rules
├── blackCookieArgs
├── blackGetArgs
├── blackPostArgs
├── blackUa
├── blackUri
└── whiteUri
/README.MD:
--------------------------------------------------------------------------------
1 | # LUAWAF
2 | A web application firewall(WAF) written in lua.
3 | ## How to use
4 | 1. Install OpenResty
5 | ```shell
6 | yum update -y
7 | yum install readline-devel pcre-devel openssl-devel perl gcc automake autoconf libtool make epel-release redis -y
8 | cd Downloads
9 | wget https://openresty.org/download/openresty-1.15.8.1.tar.gz
10 | tar -xzvf openresty-1.15.8.1.tar.gz
11 | cd openresty-1.15.8.1
12 | ./configure --with-luajit\
13 | --with-http_iconv_module
14 | gmake
15 | gmake install
16 | echo export PATH=$PATH:/usr/local/openresty/nginx/sbin >> /etc/profile
17 | source /etc/profile
18 | ```
19 | 2. Clone this project
20 | ```shell
21 | git clone https://github.com/wubonetcn/luawaf.git
22 | cp luawaf /usr/local/openresty/nginx/conf/
23 | ```
24 | 3. Edit `/usr/local/openresty/nginx/conf/nginx.conf`
25 | ```
26 | vim /usr/local/openresty/nginx/conf/nginx.conf
27 | ```
28 | /usr/local/openresty/nginx/conf/nginx.conf
29 | ```
30 | #user nobody;
31 | worker_processes 1;
32 |
33 | error_log logs/error.log;
34 | #error_log logs/error.log notice;
35 | #error_log logs/error.log info;
36 |
37 | #pid logs/nginx.pid;
38 |
39 |
40 | events {
41 | worker_connections 1024;
42 | }
43 |
44 |
45 | http {
46 | include mime.types;
47 | default_type application/octet-stream;
48 |
49 | #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
50 | # '$status $body_bytes_sent "$http_referer" '
51 | # '"$http_user_agent" "$http_x_forwarded_for"';
52 |
53 | #access_log logs/access.log main;
54 |
55 | sendfile on;
56 | #tcp_nopush on;
57 |
58 | #keepalive_timeout 0;
59 | keepalive_timeout 65;
60 |
61 | #gzip on;
62 |
63 | server {
64 | listen 80;
65 | server_name localhost;
66 |
67 | #charset koi8-r;
68 |
69 | #access_log logs/host.access.log main;
70 |
71 | location / {
72 | root html;
73 | index index.html index.htm;
74 | }
75 |
76 | #error_page 404 /404.html;
77 |
78 | # redirect server error pages to the static page /50x.html
79 | #
80 | error_page 500 502 503 504 /50x.html;
81 | location = /50x.html {
82 | root html;
83 | }
84 |
85 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80
86 | #
87 | #location ~ \.php$ {
88 | # proxy_pass http://127.0.0.1;
89 | #}
90 |
91 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
92 | #
93 | #location ~ \.php$ {
94 | # root html;
95 | # fastcgi_pass 127.0.0.1:9000;
96 | # fastcgi_index index.php;
97 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
98 | # include fastcgi_params;
99 | #}
100 |
101 | # deny access to .htaccess files, if Apache's document root
102 | # concurs with nginx's one
103 | #
104 | #location ~ /\.ht {
105 | # deny all;
106 | #}
107 | }
108 |
109 |
110 | # another virtual host using mix of IP-, name-, and port-based configuration
111 | #
112 | #server {
113 | # listen 8000;
114 | # listen somename:8080;
115 | # server_name somename alias another.alias;
116 |
117 | # location / {
118 | # root html;
119 | # index index.html index.htm;
120 | # }
121 | #}
122 |
123 |
124 | # HTTPS server
125 | #
126 | #server {
127 | # listen 443 ssl;
128 | # server_name localhost;
129 |
130 | # ssl_certificate cert.pem;
131 | # ssl_certificate_key cert.key;
132 |
133 | # ssl_session_cache shared:SSL:1m;
134 | # ssl_session_timeout 5m;
135 |
136 | # ssl_ciphers HIGH:!aNULL:!MD5;
137 | # ssl_prefer_server_ciphers on;
138 |
139 | # location / {
140 | # root html;
141 | # index index.html index.htm;
142 | # }
143 | #}
144 |
145 |
146 | lua_package_path "/usr/local/openresty/nginx/conf/luawaf/?.lua;/usr/local/openresty/lualib/?.lua;;";
147 | lua_shared_dict limit 10m;
148 | init_by_lua_file /usr/local/openresty/nginx/conf/luawaf/init.lua;
149 | access_by_lua_file /usr/local/openresty/nginx/conf/luawaf/main.lua;
150 |
151 | }
152 | ```
153 | 4. Run Nginx
154 | ```
155 | nginx
156 | ```
157 | Enjoy it!
158 | ## Fix bug
159 | Please read this post.
160 | [《openresty的unescape_uri函数处理百分号后面字符的小特性》](https://www.cnxct.com/openresty-unescape_uri-feature-to-decode-char-after-percent-sign/)
161 |
162 |
--------------------------------------------------------------------------------
/config.lua:
--------------------------------------------------------------------------------
1 | --WAF日志开关
2 | WafLog = "on"
3 | --日志文件夹
4 | LogPath = "/usr/local/openresty/nginx/logs/luawaf/"
5 | RulePath = "/usr/local/openresty/nginx/conf/luawaf/rules/"
6 | --配置白名单IP列表
7 | WhiteIpList = { "127.0.0.1" }
8 | --配置黑名单IP列表
9 | BlackIpList = { "1.0.0.1" }
10 | --是否开启CC攻击检查
11 | CheckCC = "off"
12 | --配置CC攻击频率阈值,次/秒
13 | CCFrequency = "100/60"
14 | --是否开启白名单URI检查
15 | CheckWhiteUri = "on"
16 | --是否开启黑名单URI检查
17 | CheckBlackUri = "on"
18 | --是否开启黑名单Cookie检查
19 | CheckBlackCookie = "on"
20 | --是否开启POST黑名单信息检查
21 | CheckBlackPostArgs = "on"
22 | --配置POST文件上传后缀黑名单
23 | BlackFileExt = { "php", "jsp" }
24 | --是否返回拦截页面
25 | Return403Page = "on"
26 | --配置403页面
27 | html = [[
28 |
29 |
30 | 403 Forbidden
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 403
44 | 请求被拦截
45 | 你可能在网站管理员的黑名单里,或者你发起了不正常的请求。
46 | 联系管理员
47 | |
48 |
49 |
50 | |
51 |
52 |
53 |
54 |
55 | ]]
56 |
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | 该版本的WAF默认使用redis存储拦截规则等配置信息,后期测试如性能受限将改用文件存储。生产环境中,Redis中的规则将由控制台控制。
3 | Redis存储拦截规则的优点是能够实时应用规则改动,缺点是比文件存储消耗资源多。是一个空间换时间的举措。
4 | --]]
5 | --[[
6 | 该模块的主要作用是在nginx启动时加载变量和函数。
7 | --]]
8 |
9 | require("config")
10 | --------------------------------------------------------初始化
11 | --规则读取函数
12 | function read_rule(var)
13 | file = io.open(rulePath .. '/' .. var, "r")
14 | if file == nil then
15 | return
16 | end
17 | t = {}
18 | for line in file:lines() do
19 | table.insert(t, line)
20 | end
21 | file:close()
22 | return (t)
23 | end
24 | local optionIsOn = function(options)
25 | return options == "on" and true or false
26 | end
27 | -------------------------------------------------------变量集合
28 | whiteIpList = WhiteIpList
29 | blackIpList = BlackIpList
30 | CCFrequency = CCFrequency
31 | blackFileExt = BlackFileExt
32 | logPath = LogPath
33 | rulePath = RulePath
34 | checkBlackUri = optionIsOn(CheckBlackUri)
35 | checkBlackPostArgs = optionIsOn(CheckBlackPostArgs)
36 | checkBlackCookie = optionIsOn(CheckBlackCookie)
37 | checkWhiteUri = optionIsOn(CheckWhiteUri)
38 | PathInfoFix = optionIsOn(PathInfoFix)
39 | wafLog = optionIsOn(WafLog)
40 | checkCC = optionIsOn(CheckCC)
41 | return403Page = optionIsOn(Return403Page)
42 | blackUri = read_rule("blackUri")
43 | blackGetArgs = read_rule("blackGetArgs")
44 | blackUa = read_rule("blackUa")
45 | whiteUri = read_rule("whiteUri")
46 | blackPostArgs = read_rule("blackPostArgs")
47 | blackCookieArgs = read_rule("blackCookieArgs")
48 |
49 | -------------------------------------------------------变量集合
50 |
51 | -------------------------------------------------------工具函数
52 | --获取客户端IP
53 | function getClientIp()
54 | IP = ngx.var.remote_addr
55 | if IP == nil then
56 | IP = "unknown"
57 | end
58 | return IP
59 | end
60 |
61 | --写本地文件
62 | function write(file, msg)
63 | local fd = io.open(file, "ab")
64 | if fd == nil then
65 | return
66 | end
67 | fd:write(msg)
68 | fd:flush()
69 | fd:close()
70 | end
71 |
72 | --记录日志
73 | function log(method, url, data, Reason)
74 | if wafLog then
75 | local realIp = getClientIp()
76 | local ua = ngx.var.http_user_agent
77 | local servername = ngx.var.server_name
78 | local time = ngx.localtime()
79 | if ua then
80 | line = realIp .. " [" .. time .. "] \"" .. method .. " " .. servername .. url .. "\" \"" .. data .. "\" \"" .. ua .. "\" \"" .. Reason .. "\"\n"
81 | else
82 | line = realIp .. " [" .. time .. "] \"" .. method .. " " .. servername .. url .. "\" \"" .. data .. "\" - \"" .. Reason .. "\"\n"
83 | end
84 | local filename = logPath .. '/' .. servername .. "_" .. ngx.today() .. ".log"
85 | write(filename, line)
86 | end
87 | end
88 |
89 | --输出禁止访问页面
90 | function say_html()
91 | if return403Page then
92 | ngx.header.content_type = "text/html"
93 | ngx.say(html)
94 | ngx.exit(403)
95 | end
96 | end
97 |
98 | --检查黑名单文件后缀名
99 | function fileExtCheck(ext)
100 | local items = Set(blackFileExt)
101 | ext = string.lower(ext)
102 | if ext then
103 | for rule in pairs(items) do
104 | if ngx.re.match(ext, rule, "isjo") then
105 | return true
106 | end
107 | end
108 | end
109 | return false
110 | end
111 |
112 | --工具函数,转换table的格式
113 | function Set (list)
114 | local set = {}
115 | for _, l in ipairs(list) do
116 | set[l] = true
117 | end
118 | return set
119 | end
120 |
121 | --检查POST请求中URL编码的数据
122 | function checkEscapedData(data)
123 | for _, rule in pairs(blackPostArgs) do
124 | if rule ~= "" and data ~= "" and ngx.re.match(ngx.unescape_uri(ngx.unescape_uri(ngx.unescape_uri(data))), rule, "isjo") then
125 |
126 | return true
127 | end
128 | end
129 | return false
130 | end
131 |
132 | --检查是否是multipart/form-data编码的数据
133 | function isMultipartFormData()
134 | local header = ngx.req.get_headers()["content-type"]
135 | if not header then
136 | return nil
137 | end
138 |
139 | if type(header) == "table" then
140 | header = header[1]
141 | end
142 |
143 | --这里修改了源程序的匹配规则,因为当前浏览器已经不再在boundary=后面加""了,故删去。
144 | --为何这样匹配?因为浏览器表单设置enctype="multipart/form-data"时,传任何参数或者文件都会在content-type请求头里加入一个字符串。
145 | --这个字符串类似content-type:multipart/form-data; boundary=--------------------------924633240806182131117927
146 | --enctype="x-www-form-urlencoded"时,也可以传文件或者键值对给后端,但是传文件的功能受限,单独传文件将以二进制形式传递文件,除非文件中再次声明文件名,否则文件名将被忽略,也就是说除非后端程序刻意读取发送者刻意加的文件名,这种编码不会传递文件名,主动读取属于后端白名单需求,不予保护。
147 | local m = string.match(header, ";%s*boundary=([^\"]+)")
148 | if m then
149 | return m
150 | end
151 |
152 | return string.match(header, ";%s*boundary=([^\",;]+)")
153 | end
154 |
155 |
156 | -------------------------------------------------------工具函数
157 | ---
158 | ---函数顺序:按照检查顺序
159 | ---
160 | -------------------------------------------------------函数集合
161 | --白名单IP检查
162 | function isWhiteIp()
163 | if next(whiteIpList) ~= nil then
164 | for _, ip in pairs(whiteIpList) do
165 | if getClientIp() == ip then
166 | return true
167 | end
168 | end
169 | end
170 | return false
171 | end
172 |
173 | --黑名单IP检查
174 | function isBlackIp()
175 | if next(blackIpList) ~= nil then
176 | for _, ip in pairs(blackIpList) do
177 | if getClientIp() == ip then
178 |
179 | return true
180 |
181 | end
182 | end
183 | end
184 | return false
185 | end
186 |
187 | --CC攻击检查
188 | function isCcAttack()
189 | --开启检查CC开关才检查CC
190 | if checkCC then
191 | local uri = ngx.var.uri
192 | --CCcount/CCseconds,CCseconds 秒内有CCcount访问
193 | --CC阈值
194 | CCcount = tonumber(string.match(CCFrequency, '(.*)/'))
195 | --CC时间阈值
196 | CCseconds = tonumber(string.match(CCFrequency, '/(.*)'))
197 | --特定IP访问特定网址作为判断CC的键
198 | local token = getClientIp() .. uri
199 | --创建一个nginx字典,用来记录访问次数
200 | local limit = ngx.shared.limit
201 | --获取键对应的访问次数
202 | local req, _ = limit:get(token)
203 | --第一次访问或者到时低频访问req为nil
204 | if req then
205 | --CC时间阈值内访问次数大于次数阈值,就是CC攻击
206 | if req > CCcount then
207 | return true
208 | --未达到阈值,访问次数加一
209 | else
210 | limit:incr(token, 1)
211 | end
212 | else
213 | --第一次或者超过阈值键失效则新建记录。
214 | --在字典中存入token作为键,初始值为1,生存时间为CC时间阈值
215 | limit:set(token, 1, CCseconds)
216 | end
217 | end
218 | return false
219 | end
220 |
221 | --检查是不是扫描器
222 | function isScanner()
223 | --[[
224 | Acunetix-Aspect
225 | Acunetix-Aspect-Password
226 | Acunetix-Aspect-Queries
227 | 以上三个是AWVS漏洞扫描工具的自带的请求头参数,是其特有字段。可借助这种特征识别出AWVS并及时阻止扫描。
228 | 以上三个字段有值,即发现AWVS扫描器。
229 | --]]
230 | if type(ngx.req.get_headers()['http_Acunetix_Aspect']) ~= "nil" then
231 | return true
232 | elseif type(ngx.req.get_headers()['http_Acunetix-Aspect-Password']) ~= "nil" then
233 | return true
234 | elseif type(ngx.req.get_headers()['http_Acunetix-Aspect-Queries']) ~= "nil" then
235 | return true
236 | --X_Scan_Memo是X-Scan漏洞扫描器的特征字段,发现其有值,即发现X-Scan漏洞扫描器。
237 | elseif type(ngx.req.get_headers()['http_X_Scan_Memo']) ~= "nil" then
238 | return true
239 | else
240 | return false
241 | end
242 | end
243 |
244 | --白名单URI检查
245 | function isWhiteUri()
246 | if checkWhiteUri then
247 | if whiteUri ~= nil then
248 | for _, rule in pairs(whiteUri) do
249 | if ngx.re.match(ngx.var.uri, rule, "isjo") then
250 | return true
251 | end
252 | end
253 | end
254 | end
255 | return false
256 | end
257 |
258 | --检查是否是黑名单中的UA
259 | function isBlackUa()
260 | local ua = ngx.var.http_user_agent
261 | if ua ~= nil then
262 | for _, rule in pairs(blackUa) do
263 | if rule ~= "" and ngx.re.match(ua, rule, "isjo") then
264 |
265 | return true
266 | end
267 | end
268 | end
269 | return false
270 | end
271 |
272 | --检查是否是黑名单中的URI
273 | function isBlackUri()
274 | if checkBlackUri then
275 | for _, rule in pairs(blackUri) do
276 | if rule ~= "" and ngx.re.match(ngx.var.request_uri, rule, "isjo") then
277 |
278 | return true
279 | end
280 | end
281 | end
282 | return false
283 | end
284 |
285 | --检查GET请求中是否有恶意字符串
286 | function isBlackGetArgs()
287 | for _, rule in pairs(blackGetArgs) do
288 | local args = ngx.req.get_uri_args()
289 | for key, val in pairs(args) do
290 | if type(val) == 'table' then
291 | local t = {}
292 | for k, v in pairs(val) do
293 | if v == true then
294 | v = ""
295 | end
296 | table.insert(t, v)
297 | end
298 | data = table.concat(t, " ")
299 | else
300 | data = val
301 | end
302 | if data and type(data) ~= "boolean" and rule ~= "" and ngx.re.match(ngx.unescape_uri(ngx.unescape_uri(ngx.unescape_uri(data))), rule, "isjo") then
303 | return true
304 | end
305 | end
306 | end
307 | return false
308 | end
309 |
310 | --检查Cookie中是否有恶意字符串
311 | function isBlackCookie()
312 | local ck = ngx.var.http_cookie
313 | if checkBlackCookie and ck then
314 | for _, rule in pairs(blackCookieArgs) do
315 | if rule ~= "" and ngx.re.match(ck, rule, "isjo") then
316 | return true
317 | end
318 | end
319 | end
320 | return false
321 | end
322 |
323 | function isBlackPostArgs()
324 | if checkBlackPostArgs then
325 | if ngx.req.get_method() == "POST" then
326 | --如果客户端使用了multipart/form-data,则说明表单中可能有文件,进入以下检查。
327 | if isMultipartFormData() then
328 | if isBlackMultipartFormData() then
329 | return true
330 | else
331 | return false
332 | end
333 | --如果客户端使用了x-www-form-urlencoded,一般说明请求中没有文件,只有键值对。如果是传输文件,那么文件没有文件名,只有文件二进制数据,也不排除后端程序使用这种方式传输文件,从文件二进制数据中获取文件名,这种后端主动白名单行为不在WAF保护范围内。
334 | else
335 | if isBlackXWwwFormUrlencodedData() then
336 | return true
337 | else
338 | return false
339 | end
340 |
341 | end
342 | end
343 | else
344 | return false
345 | end
346 | end
347 |
348 | --检查POST的multipart/form-data表单中是否有恶意字符串
349 | function isBlackMultipartFormData()
350 | --打开一个SOCKET用来接收数据
351 | local sock, err = ngx.req.socket()
352 | if not sock then
353 | return false
354 | end
355 | --为当前请求创建一个新请求体并初始化一个缓存区,大小为128KB。
356 | ngx.req.init_body(128 * 1024)
357 | --设置接收超时时间0,一旦接收不到数据就停止,不等待。
358 | sock:settimeout(0)
359 | --从headers里获取请求体大小
360 | local content_length = nil
361 | content_length = tonumber(ngx.req.get_headers()['content-length'])
362 | --默认接收块大小是4KB
363 | local chunk_size = 4096
364 | --如果请求体比默认接收块还小,就接收请求体大小的数据即可。
365 | if content_length < chunk_size then
366 | chunk_size = content_length
367 | end
368 | --size是当前已经接收的请求体总大小
369 | local size = 0
370 | --要是当前接收的请求体大小小于总大小就继续接收
371 | while size < content_length do
372 | --从打开的socket里读取默认接受块大小的数据
373 | --兼顾TCP和UDP的写法,UDP没有partial只有两个参数
374 | local data, err, partial = sock:receive(chunk_size)
375 | data = data or partial
376 | if not data then
377 | return false
378 | end
379 | --将读取到的数据追加到请求体里
380 | ngx.req.append_body(data)
381 | --检查当前读到的数据块
382 | --当前POST请求的某一参数在黑名单中,即checkEscapedData(data)为true,执行then后语句,记录恶意请求信息,返回错误页面,请求截断;当前POST请求的某一参数不在黑名单中,checkEscapedData(data)为false,程序向下运行;
383 | --检查不通过,结束读取,返回错误页面。
384 | if checkEscapedData(data) then
385 | return true
386 | end
387 | --给当前已经接收的请求体总大小加上刚刚读取到的数据大小。
388 | size = size + string.len(data)
389 | --使用效率较高的nginx的正则匹配,原作者代码中多写了一个\,已改正。
390 | --这一步是为了得到文件的后缀名
391 | local m = ngx.re.match(data, [[Content-Disposition: form-data;(.+)filename="(.+)\.(.*)"]], 'ijo')
392 | --原作者的规则并不能匹配同一表单中上传的多个文件,试了一早上,并没有改成匹配多文件的。
393 | --这里遗留一个BUG,待修复。
394 | if m then
395 | if fileExtCheck(m[3]) then
396 | return true
397 | end
398 | checkNext = false
399 | --如果POST请求中没有文件,将执行以下检查
400 | else
401 | --如果POST请求中有键值对,继续检查
402 | if ngx.re.match(data, "Content-Disposition:(.+)", 'isjo') then
403 | checkNext = true
404 | end
405 | if checkNext then
406 | --当前POST请求的某一参数在黑名单中,即checkEscapedData(data)为true,执行then后语句,记录恶意请求信息,返回错误页面,请求截断;当前POST请求的某一参数不在黑名单中,checkEscapedData(data)为false,程序向下运行;
407 | if checkEscapedData(data) then
408 |
409 | return true
410 | end
411 | end
412 | end
413 | --将最后剩余的请求体大小和当前默认块大小比较,如果剩余的比4KB还小最后一次就只读取剩余大小的数据。
414 | local less = content_length - size
415 | if less < chunk_size then
416 | chunk_size = less
417 | end
418 | end
419 | --结束multipart/form-data编码的POST表单读取
420 | ngx.req.finish_body()
421 | end
422 |
423 | --检查POST的x-www-form-urlencoded表单中是否有恶意字符串
424 | function isBlackXWwwFormUrlencodedData()
425 | --读取POST参数时,要先读取请求体
426 | ngx.req.read_body()
427 | --获取POST键值对
428 | local args = ngx.req.get_post_args()
429 | --如果键值对为空,直接通过
430 | if not args then
431 | return false
432 | end
433 | --有值,遍历检查每一个键值对
434 | for key, val in pairs(args) do
435 | --如果值时一个table或者数组,那么把其中的所有值都用,连接成一个字符串data;
436 | if type(val) == "table" then
437 | --三维数组跳过检测,这里遗留一个BUG,以后优化。
438 | if type(val[1]) == "table" then
439 | return false
440 | end
441 | data = table.concat(val, ", ")
442 | --如果值是一个字符串,那么就执行检查。
443 | else
444 | data = val
445 | end
446 | --先检查value再检查key
447 | if data and type(data) ~= "boolean" then
448 | if checkEscapedData(data) then
449 | return true
450 | end
451 | if checkEscapedData(key) then
452 | return true
453 | end
454 | return false
455 | end
456 | end
457 |
458 | end
459 |
460 |
461 |
462 |
463 |
464 |
465 |
--------------------------------------------------------------------------------
/main.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | 遗留BUG:
3 | 1. [《openresty的unescape_uri函数处理百分号后面字符的小特性》](https://www.cnxct.com/openresty-unescape_uri-feature-to-decode-char-after-percent-sign/) 未解决 可以按照该篇文章中的方法重新编译OpenResty解决。
4 | ]]
5 | if isWhiteIp() then
6 | elseif isBlackIp() then
7 | ngx.exit(403)
8 | elseif isCcAttack() then
9 | ngx.exit(503)
10 | elseif isScanner() then
11 | ngx.exit(444)
12 | elseif isWhiteUri() then
13 | elseif isBlackUa() then
14 | log('UA', ngx.var.request_uri, "-", "当前UA在黑名单中")
15 | say_html()
16 | elseif isBlackUri() then
17 | log('GET', ngx.var.request_uri, "-", "当前请求的URI在黑名单中")
18 | say_html()
19 | elseif isBlackGetArgs() then
20 | log('GET', ngx.var.request_uri, "-", "当前GET请求的某一参数在黑名单中")
21 | say_html()
22 | elseif isBlackCookie() then
23 | log('Cookie', ngx.var.request_uri, "-", "当前cookie中的某一参数在黑名单中")
24 | say_html()
25 | elseif isBlackPostArgs() then
26 | log('POST', ngx.var.request_uri, data, "当前POST请求的某一参数在黑名单中")
27 | say_html()
28 | else
29 | return
30 | end
31 |
--------------------------------------------------------------------------------
/rules/blackCookieArgs:
--------------------------------------------------------------------------------
1 | \.\./
2 | \:\$
3 | \$\{
4 | select.+(from|limit)
5 | (?:(union(.*?)select))
6 | having|rongjitest
7 | sleep\((\s*)(\d*)(\s*)\)
8 | benchmark\((.*)\,(.*)\)
9 | base64_decode\(
10 | (?:from\W+information_schema\W)
11 | (?:(?:current_)user|database|schema|connection_id)\s*\(
12 | (?:etc\/\W*passwd)
13 | into(\s+)+(?:dump|out)file\s*
14 | group\s+by.+\(
15 | xwork.MethodAccessor
16 | (?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\(
17 | xwork\.MethodAccessor
18 | (gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/
19 | java\.lang
20 | \$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[
21 |
--------------------------------------------------------------------------------
/rules/blackGetArgs:
--------------------------------------------------------------------------------
1 | \.\./
2 | \:\$
3 | \$\{
4 | select.+(from|limit)
5 | (?:(union(.*?)select))
6 | having|rongjitest
7 | sleep\((\s*)(\d*)(\s*)\)
8 | benchmark\((.*)\,(.*)\)
9 | base64_decode\(
10 | (?:from\W+information_schema\W)
11 | (?:(?:current_)user|database|schema|connection_id)\s*\(
12 | (?:etc\/\W*passwd)
13 | into(\s+)+(?:dump|out)file\s*
14 | group\s+by.+\(
15 | xwork.MethodAccessor
16 | (?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\(
17 | xwork\.MethodAccessor
18 | (gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/
19 | java\.lang
20 | \$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[
21 | \<(iframe|script|body|img|layer|div|meta|style|base|object|input|svg|a)
22 | (onmouseover|onerror|onload)\=
23 |
--------------------------------------------------------------------------------
/rules/blackPostArgs:
--------------------------------------------------------------------------------
1 | select.+(from|limit)
2 | (?:(union(.*?)select))
3 | having|rongjitest
4 | sleep\((\s*)(\d*)(\s*)\)
5 | benchmark\((.*)\,(.*)\)
6 | base64_decode\(
7 | (?:from\W+information_schema\W)
8 | (?:(?:current_)user|database|schema|connection_id)\s*\(
9 | (?:etc\/\W*passwd)
10 | into(\s+)+(?:dump|out)file\s*
11 | group\s+by.+\(
12 | xwork.MethodAccessor
13 | (?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\(
14 | xwork\.MethodAccessor
15 | (gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/
16 | java\.lang
17 | \$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[
18 | \<(iframe|script|body|img|layer|div|meta|style|base|object|input|svg|a)
19 | (onmouseover|onerror|onload)\=
20 |
--------------------------------------------------------------------------------
/rules/blackUa:
--------------------------------------------------------------------------------
1 | (HTTrack|harvest|audit|dirbuster|pangolin|nmap|sqln|-scan|hydra|Parser|libwww|BBBike|sqlmap|w3af|owasp|Nikto|fimap|havij|PycURL|zmeu|BabyKrokodil|netsparker|httperf|bench| SF/)
2 |
--------------------------------------------------------------------------------
/rules/blackUri:
--------------------------------------------------------------------------------
1 | \.(svn|htaccess|bash_history)
2 | \.(bak|inc|old|mdb|sql|backup|java|class)$
3 | (vhost|bbs|host|wwwroot|www|site|root|hytop|flashfxp).*\.rar
4 | (phpmyadmin|jmx-console|jmxinvokerservlet)
5 | java\.lang
6 | /(attachments|upimg|images|css|uploadfiles|html|uploads|templets|static|template|data|inc|forumdata|upload|includes|cache|avatar)/(\\w+).(php|jsp)
7 |
--------------------------------------------------------------------------------
/rules/whiteUri:
--------------------------------------------------------------------------------
1 | ^/123/$
2 |
--------------------------------------------------------------------------------