diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 3ce7fdb..dbefb79 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -2,9 +2,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
-
@@ -26,22 +38,38 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -50,8 +78,8 @@
-
-
+
+
@@ -60,8 +88,8 @@
-
-
+
+
@@ -70,8 +98,8 @@
-
-
+
+
@@ -96,10 +124,11 @@
@@ -110,10 +139,10 @@
DEFINITION_ORDER
-
-
-
-
+
+
+
+
@@ -276,6 +305,12 @@
+ project
+
+
+
+
+
@@ -421,27 +456,27 @@
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
+
+
@@ -461,7 +496,7 @@
-
+
@@ -469,18 +504,34 @@
-
+
-
-
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -493,45 +544,166 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..75a32bf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+### nginx lua waf
+
+##### 参考
+1. https://github.com/loveshell/ngx_lua_waf
+2. https://github.com/p0pr0ck5/lua-resty-waf
+
+
+#### 使用
+1. 安装Nginx和lua插件 或者直接安装 openresty
+2. git clone
+3. nginx.conf 配置文件
+ http段
+ ...
+
+ lua_package_path "/data/server/nginx/conf/waf/?.lua";
+ lua_shared_dict limit 10m;
+ ...
+
+4. 在location中使用
+
+ location / {
+ access_by_lua '
+ local lua_waf = require "core"
+ local waf = lua_waf:new("default")
+ waf:set_option("cc_rate", "2/60")
+ waf:set_option("active", true)
+ waf:set_option("white_ip_list", {"192.168.128.0/24", "127.0.0.1"})
+ waf:run()
+ ';
+ ...
+ }
+
+5. reload
+
+#### 说明
+— 默认配置文件
+
+ _M.defaults = {
+ active = false,
+ cc_deny = true,
+ cc_rate = "100/600",
+ cc_deny_seconds = 600,
+ cc_deny_code = 404,
+ log_path = "/tmp/nginx_waf.log",
+ white_ip_list = {},
+ black_ip_list = {},
+ black_return_code = 403,
+ }
+
+- 单独设置
+
+ waf:set_option("cc_rate", "2/60")
+
+- 问题排查
+ nginx error日志 和 log_path
+
\ No newline at end of file
diff --git a/config.lua b/config.lua
index a3b4d48..333bd54 100644
--- a/config.lua
+++ b/config.lua
@@ -11,7 +11,6 @@ _M.version = '0.1.0'
_M.defaults = {
- debug = true,
active = false,
cc_deny = true,
cc_rate = "100/600",
diff --git a/core.lua b/core.lua
index 2a64bda..b45b8b9 100644
--- a/core.lua
+++ b/core.lua
@@ -15,6 +15,7 @@ local config = require "config"
local iputils = require "iputils"
local mt = {__index=_M }
local limit = ngx.shared.limit
+local _cidr_cache = {}
local function get_client_ip()
local ip = get_headers()["X-Real-IP"]
@@ -71,11 +72,11 @@ function _M.deny_cc(self)
return false
end
elseif req == max_visit then
+ self:log("[Deny_cc] Block "..token)
+ limit:incr(token, 1)
if self.config.active then
ngx.exit(self.config.cc_deny_code)
end
- self:log("[Deny_cc] Block "..token)
- limit:incr(token, 1)
return true
else
limit:incr(token, 1)
@@ -85,6 +86,32 @@ function _M.deny_cc(self)
end
end
+function cidr_match(ip, cidr_pattern)
+ local t = {}
+ local n = 1
+
+ if (type(cidr_pattern) ~= "table") then
+ cidr_pattern = { cidr_pattern }
+ end
+
+ for _, v in ipairs(cidr_pattern) do
+ -- try to grab the parsed cidr from out module cache
+ local cidr = _cidr_cache[v]
+
+ -- if it wasn't there, compute and cache the value
+ if (not cidr) then
+ local lower, upper = iputils.parse_cidr(v)
+ cidr = { lower, upper }
+ _cidr_cache[v] = cidr
+ end
+
+ t[n] = cidr
+ n = n + 1
+ end
+
+ return iputils.ip_in_cidrs(ip, t), ip
+end
+
function _M.log(self, msg)
ngx.log(ngx.WARN, self.config.log_path)
if log_inited[self.config.log_path] == nil then
@@ -110,12 +137,10 @@ function _M.in_white_ip_list(self)
local white_ip_list = self.config.white_ip_list
if next(white_ip_list) ~= nil then
- for _, wip in pairs(white_ip_list) do
- if ip == wip or iputils.ip_in_cidrs(ip, wip) then
+ if cidr_match(ip, white_ip_list) then
limit:set(white_ip_token, true, 3600)
self:log("[White_ip] In white list passed: "..ip)
return true
- end
end
end
return false
@@ -135,15 +160,13 @@ function _M.in_black_ip_list(self)
local black_ip_list = self.config.black_ip_list
if next(black_ip_list) ~= nil then
- for _, bip in pairs(black_ip_list) do
- if ip == bip or iputils.ip_in_cidrs(ip, bip) then
+ if cidr_match(ip, black_ip_list) then
limit:set(block_ip_token, true, 3600)
self:log("[Black_ip] In black list denied: "..ip)
if self.config.active then
ngx.exit(self.config.black_return_code)
end
return true
- end
end
end
return false
diff --git a/old/README.md b/old/README.md
deleted file mode 100644
index 27bf878..0000000
--- a/old/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-## ngx_lua_waf
-基于 loveshell [nginx-lua-waf](https://github.com/loveshell/ngx_lua_waf)更改
-
-### 使用方法:
-1. nginx安装lua模块,不再详述
-2. 下载模块
- cd /data/server/nginx/conf
- git clone https://github.com/ibuler/ngx_lua_waf.git waf
-2. nginx.conf 添加参数
- lua_package_path /data/server/nginx/conf/waf/?.lua; # 模块位置
- lua_shared_dict limit 10m; # 设置lua使用内存, 根据访问量设置合适值
-3. location或server设置访问控制
- access_by_lua_file /data/server/nginx/conf/waf/entry.lua; # 可以copy一份到不同的配置中,单独修改其配置文件
-
-## 文件说明
-- config.lua 默认配置文件
-- entry.lua access控制入口样例文件
-- init.lua 函数所在文件,都会调用该文件
-- wafconf 暂时没有使用,将来开发继续完成
-
-
-### 参数说明
-- debug: 调试阶段开始debug,显示debug信息
-- cc_deny: 开启cc_deny,控制访问量
-- cc_rate: 10/60 意思为 60s内访问10次,超过频率会被block掉
-- cc_deny_seconds: 达到阈值后,禁止访问的时间
-
-
diff --git a/old/config.lua b/old/config.lua
deleted file mode 100644
index 67d330c..0000000
--- a/old/config.lua
+++ /dev/null
@@ -1,23 +0,0 @@
-debug = false
--- rule_path = "/data/server/nginx/conf/waf/wafconf/"
--- url_check = false
--- url_write_check = false
--- args_check = false
--- ua_check = false
--- ua_write_check = false
--- cookie_check = false
--- post_check = false
-
--- black_file_ext = {"php", "jsp"}
--- attack_log = false
--- attach_log_dir = "/data/logs/waf/"
-
--- redirect = false
--- redirect_url = "http://www.baidu.com"
-ip_check = false
-ip_white_list = {} -- {'192.168.1.*', '127.0.0.1'}
-ip_black_list = {} -- {'0.0.0.0', '106.2.34.29'}
-
-cc_deny = false
-cc_rate = "100/60"
-cc_deny_seconds = "600"
diff --git a/old/entry.lua b/old/entry.lua
deleted file mode 100644
index 7bcea95..0000000
--- a/old/entry.lua
+++ /dev/null
@@ -1,16 +0,0 @@
---------- Global default config -------
-require 'config'
---------- Local config setting --------
-debug = true
-
-cc_deny = false
-cc_rate = '10/60'
-ip_check = true
-ip_white_list = {}
-ip_black_list = {}
-
---------- Access control limit --------
-if ip_check and (whiteIP(ip_white_list, debug) or blackIP(ip_black_list, debug)) then
-elseif cc_deny and denyCC(cc_rate, cc_deny_seconds, debug) then
-else return
-end
diff --git a/old/init.lua b/old/init.lua
deleted file mode 100644
index 7853b02..0000000
--- a/old/init.lua
+++ /dev/null
@@ -1,313 +0,0 @@
-local match = string.match
-local ngx_match = ngx.re.match
-local unescape = ngx.unescape_uri
-local get_headers = ngx.req.get_headers
-
-function getClientIp()
- IP = get_headers()["X-Real-IP"]
- if IP == nil then
- IP = ngx.var.remote_addr
- end
- 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, tag)
- if attack_log 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.."\" \""..tag.."\"\n"
- else
- line = realIp.." ["..time.."] \""..method.." "..servername..url.."\" \""..data.."\" - \""..tag.."\"\n"
- end
- local filename = logpath..'/'..servername.."_"..ngx.today().."_sec.log"
- write(filename, line)
- end
-end
-
------------------------------------- 规则读取函数 -----------------------------------------
--- function readRule(var)
--- file = io.open(rule_path..'/'..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
-
--- url_rules = readRule('url')
--- white_url_rules = readRule('white_url')
--- args_rules = readRule('args')
--- ua_rules = readRule('user_agent')
--- post_rules = readRule('post')
--- cookie_rules = readRule('cookie')
-
-
-function debugSay(msg, debug)
- if debug then
- ngx.header.content_type = "text/html"
- ngx.status = ngx.HTTP_FORBIDDEN
- ngx.say(msg)
- ngx.exit(ngx.status)
- end
-end
-
-
--- function whiteURLCheck()
--- if white_url_rules ~= nil then
--- for _, rule in pairs(white_url_rules) do
--- if ngx_match(ngx.var.uri, rule, "isjo") then
--- return true
--- end
--- end
--- end
--- return false
--- end
-
-
--- function fileExtCheck(ext, black_file_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
--- if attack_log then
--- log('POST',ngx.var.request_uri,"-","file attack with ext "..ext)
--- end
-
--- if debug then
--- debugSay(ngx.var.request_uri.."-".."file attack with ext: "..ext)
--- end
--- 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 checkArgs()
--- for _, rule in pairs(args_rules) do
--- local args = ngx.req.get_uri_args()
--- for key, val in pairs(args) do
--- if type(val) == 'table' then
--- if val ~= false then
--- data = table.concat(val, " ")
--- end
--- else
--- data = val
--- end
--- if data and type(data) ~= "boolean" and rule ~="" and ngx_match(unescape(data), rule, "isjo") then
--- log('GET', ngx.var.request_uri, "-", rule)
--- debugSay(ngx.var.request_uri.."-"..rule)
--- return true
--- end
--- end
--- end
--- return false
--- end
-
-
--- function url()
--- if UrlDeny then
--- for _,rule in pairs(urlrules) 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
--- 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(cc_rate, cc_deny_seconds, debug)
- local uri = ngx.var.uri
- cc_count = tonumber(string.match(cc_rate, '(.*)/'))
- cc_seconds = tonumber(string.match(cc_rate, '/(.*)'))
- local token = getClientIp()..uri
- local limit = ngx.shared.limit
- local req, _ = limit:get(token) -- 127.0.0.1_/price/v1.0: 10
- local ip = getClientIp()
- local block, _ = limit:get(ip) -- 127.0.0.1: 1
-
- if block then
- if debug then
- ngx.say('Deny by waf.')
- ngx.exit('200')
- return true
- else
- ngx.exit(404)
- end
- end
-
- if req then
- if req > cc_count then
- limit:set(ip, 1, cc_deny_seconds)
- ngx.exit(404)
- return true
- else
- limit:incr(token, 1)
- end
- else
- limit:set(token, 1, cc_seconds)
- end
- return false
-end
-
--- function get_boundary()
--- local header = get_headers()["content-type"]
--- if not header then
--- return nil
--- end
-
--- if type(header) == "table" then
--- header = header[1]
--- end
-
--- local m = match(header, ";%s*boundary=\"([^\"]+)\"")
--- if m then
--- return m
--- end
-
--- return match(header, ";%s*boundary=([^\",;]+)")
--- end
-
-function string.split(str, delimiter)
- if str==nil or str=='' or delimiter==nil then
- return nil
- end
-
- local result = {}
- for match in (str..delimiter):gmatch("(.-)"..delimiter) do
- table.insert(result, match)
- end
- return result
-end
-
-
-function innet(ip, network)
- local star = ''
- for i in string.gmatch(network, '%*') do
- star = star..i
- end
-
- local ip = string.split(ip, '%.')
- local network = string.split(network, '%.')
- if ip == nil or network == nil then
- return false
- end
-
- local ip_prefix = {}
- local network_prefix = {}
- for i=1, 4-string.len(star) do
- ip_prefix[i] = ip[i]
- network_prefix[i] = network[i]
- end
-
- ip_prefix = table.concat(ip_prefix, '.')
- network_prefix = table.concat(network_prefix, '.')
-
- if ip_prefix == network_prefix then
- return true
- else
- return false
- end
-end
-
-function whiteIP(ip_white_list, debug)
- if next(ip_white_list) ~= nil then
- ip = getClientIp()
- for _, wip in pairs(ip_white_list) do
- if ip == wip or innet(ip, wip) then
- if debug then
- ngx.say(ip.." in white list
")
- end
- return true
- end
- end
- end
- return false
-end
-
-function blackIP(ip_black_list, debug)
- if next(ip_black_list) ~= nil then
- ip = getClientIp()
- for _, bip in pairs(ip_black_list) do
- if ip == bip or ip == "0.0.0.0" or innet(ip, bip) then
- if debug then
- ngx.say(ip.." in black list
")
- end
- ngx.exit(403)
- return true
- end
- end
- end
- return false
-end
diff --git a/old/logger.lua b/old/logger.lua
deleted file mode 100644
index e69de29..0000000
diff --git a/old/waf.lua b/old/waf.lua
deleted file mode 100644
index e69de29..0000000
diff --git a/old/wafconf/args b/old/wafconf/args
deleted file mode 100644
index d5bf8e8..0000000
--- a/old/wafconf/args
+++ /dev/null
@@ -1,22 +0,0 @@
-\.\./
-\:\$
-\$\{
-select.+(from|limit)
-(?:(union(.*?)select))
-having|rongjitest
-sleep\((\s*)(\d*)(\s*)\)
-benchmark\((.*)\,(.*)\)
-base64_decode\(
-(?:from\W+information_schema\W)
-(?:(?:current_)user|database|schema|connection_id)\s*\(
-(?:etc\/\W*passwd)
-into(\s+)+(?:dump|out)file\s*
-group\s+by.+\(
-xwork.MethodAccessor
-(?: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)\(
-xwork\.MethodAccessor
-(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/
-java\.lang
-\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[
-\<(iframe|script|body|img|layer|div|meta|style|base|object|input)
-(onmouseover|onerror|onload)\=
diff --git a/old/wafconf/cookie b/old/wafconf/cookie
deleted file mode 100644
index 30554ca..0000000
--- a/old/wafconf/cookie
+++ /dev/null
@@ -1,20 +0,0 @@
-\.\./
-\:\$
-\$\{
-select.+(from|limit)
-(?:(union(.*?)select))
-having|rongjitest
-sleep\((\s*)(\d*)(\s*)\)
-benchmark\((.*)\,(.*)\)
-base64_decode\(
-(?:from\W+information_schema\W)
-(?:(?:current_)user|database|schema|connection_id)\s*\(
-(?:etc\/\W*passwd)
-into(\s+)+(?:dump|out)file\s*
-group\s+by.+\(
-xwork.MethodAccessor
-(?: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)\(
-xwork\.MethodAccessor
-(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/
-java\.lang
-\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[
diff --git a/old/wafconf/post b/old/wafconf/post
deleted file mode 100644
index 87d0946..0000000
--- a/old/wafconf/post
+++ /dev/null
@@ -1,19 +0,0 @@
-select.+(from|limit)
-(?:(union(.*?)select))
-having|rongjitest
-sleep\((\s*)(\d*)(\s*)\)
-benchmark\((.*)\,(.*)\)
-base64_decode\(
-(?:from\W+information_schema\W)
-(?:(?:current_)user|database|schema|connection_id)\s*\(
-(?:etc\/\W*passwd)
-into(\s+)+(?:dump|out)file\s*
-group\s+by.+\(
-xwork.MethodAccessor
-(?: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)\(
-xwork\.MethodAccessor
-(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/
-java\.lang
-\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[
-\<(iframe|script|body|img|layer|div|meta|style|base|object|input)
-(onmouseover|onerror|onload)\=
diff --git a/old/wafconf/url b/old/wafconf/url
deleted file mode 100644
index 67e621f..0000000
--- a/old/wafconf/url
+++ /dev/null
@@ -1,6 +0,0 @@
-\.(svn|htaccess|bash_history|git)
-\.(bak|inc|old|mdb|sql|backup|java|class)$
-(vhost|bbs|host|wwwroot|www|site|root|hytop|flashfxp).*\.rar
-(phpmyadmin|jmx-console|jmxinvokerservlet)
-java\.lang
-/(attachments|upimg|images|css|uploadfiles|html|uploads|templets|static|template|data|inc|forumdata|upload|includes|cache|avatar)/(\\w+).(php|jsp)
diff --git a/old/wafconf/user_agent b/old/wafconf/user_agent
deleted file mode 100644
index f929be2..0000000
--- a/old/wafconf/user_agent
+++ /dev/null
@@ -1 +0,0 @@
-(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/)
diff --git a/old/wafconf/white_url b/old/wafconf/white_url
deleted file mode 100644
index 4e3c654..0000000
--- a/old/wafconf/white_url
+++ /dev/null
@@ -1 +0,0 @@
-^/123/$
diff --git a/test2.lua b/test2.lua
index 7514b3a..c042e75 100644
--- a/test2.lua
+++ b/test2.lua
@@ -10,8 +10,44 @@
--local lua_waf = require "core"
local lua_waf = require "test"
local waf = lua_waf:new("test")
+local _cidr_cache = {}
print(waf.name)
+local iputils = require "iputils"
+
+function cidr_match(ip, cidr_pattern)
+ local t = {}
+ local n = 1
+
+ if (type(cidr_pattern) ~= "table") then
+ cidr_pattern = { cidr_pattern }
+ end
+
+ for _, v in ipairs(cidr_pattern) do
+ -- try to grab the parsed cidr from out module cache
+ local cidr = _cidr_cache[v]
+
+ -- if it wasn't there, compute and cache the value
+ if (not cidr) then
+ local lower, upper = iputils.parse_cidr(v)
+ cidr = { lower, upper }
+ _cidr_cache[v] = cidr
+ end
+
+ t[n] = cidr
+ n = n + 1
+ end
+
+ return iputils.ip_in_cidrs(ip, t), ip
+end
+
+a = cidr_match('192.168.128.230', {'192.168.128.0/24', '127.0.0.1'})
+
+print(a)
+
+a = cidr_match('172.16.1.1', {'172.16.1.2'})
+print(a)
+
--for k, v in pairs(waf["config"]) do
-- print(k, v)
--end