OpenResty+Lua实现WAF防火墙
若服务器收到CC攻击,一分钟内请求数大于60,则会将对方ip封进黑名单
使用浏览器发送GET请求,在路径中拼接drop database xxx等sql语句
使用apifox发送post请求,在路径中拼接drop database xxx 等sql语句
获取GET请求中的参数,将’'字符串转译成’/‘即可防止该SQL注入
获取POST请求体中的字符串,将’‘转译成’/’
GET和POST请求防止SQL注入测试成功
客户端正常访问请求能够成功响应
查看黑名单
如果1分钟内该ip请求数超过50则假如黑名单。接下来使用金额jemter工具进行压测接口
# work进程数量
worker_processes 1;
events {
# work并发连接数
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
lua_shared_dict limit 50m;
# 引用文件
lua_package_path "/usr/local/openresty/lualib/?.lua;/application/conf/waf/?.lua";
# 所有的请求都得经过access.lua进行处理
access_by_lua_file "/application/conf/waf/access.lua";
sendfile on;
keepalive_timeout 65;
# 声明缓存
lua_shared_dict my_cache 128m;
server {
# 防止GET请求方式的SQL注入
if ($query_string ~* ".*('|--|union|insert|drop|truncate|update|from|grant|exec|where|select|and|chr|mid|like|iframe|script|alert|webscan|dbappsecurity|style|WAIT
FOR|confirm|innerhtml|innertext|class).*")
{ return 403; }
#if ($uri ~* (.*)(insert|select|delete|update|count|master|truncate|declare|exec|\*|%|\')(.*)$ ) { return 403; }
if ($http_user_agent ~ ApacheBench|WebBench|Jmeter|JoeDog|Havij|GetRight|TurnitinBot|GrabNet|masscan|mail2000|github|wget|curl) { return 444; }
if ($http_user_agent ~ "Go-Ahead-Got-It") { return 444; }
if ($http_user_agent ~ "GetWeb!") { return 444; }
if ($http_user_agent ~ "Go!Zilla") { return 444; }
if ($http_user_agent ~ "Download Demon") { return 444; }
if ($http_user_agent ~ "Indy Library") { return 444; }
if ($http_user_agent ~ "libwww-perl") { return 444; }
if ($http_user_agent ~ "Nmap Scripting Engine") { return 444; }
if ($http_user_agent ~ "Load Impact") { return 444; }
if ($http_user_agent ~ "~17ce.com") { return 444; }
if ($http_user_agent ~ "WebBench*") { return 444; }
if ($http_referer ~* "17ce.com") { return 444; }
if ($http_user_agent ~* "qiyunce") { return 444; }
if ($http_user_agent ~* "YunGuanCe") { return 403; }
if ($http_referer ~* "WebBench*") { return 444; }
if ($http_user_agent ~ "BLEXBot") { return 403; }
if ($http_user_agent ~ "MJ12bot") { return 403; }
if ($http_user_agent ~ "semalt.com") { return 403; }
listen 80;
server_name localhost;
location /hello{
default_type text/html;
}
}
}
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by Administrator.
--- DateTime: 2022/8/20 15:55
---
require("init") -- 引入init.lua
function main()
if white_ip_check() then -- ip白名单
elseif black_ip_check() then -- ip黑名单
elseif sql_injection() then -- 检测sql注入
elseif cc_defense() then -- 检测cc攻击
else
return
end
end
main()
init文件主要是一些处理各个功能的函数
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by Administrator.
--- DateTime: 2022/8/20 15:56
---
require("config") -- 引入config.lua
require("lib") -- 引入lib.lua
--local rulematch = ngx.re.find
--local unescape = ngx.unescape_uri
function white_ip_check()
if config_white_ip_check == "on" then -- 如果开启ip检测
local CLIENT_IP = "192.168.217.1" -- 获取ip
local WHITE_IP_FILE = get_rule('white_ip.txt') -- 读取白名单
if WHITE_IP_FILE ~= nil then
for _, rule in ipairs(WHITE_IP_FILE) do
if CLIENT_IP == rule then -- 如果客户端ip是白名单则记录访问日志
log_record('White_IP',ngx.var_request_uri,"_","_")
return true
end
end
end
end
end
function black_ip_check()
if config_black_ip_check == "on" then -- 是否开启黑名单检测
local CLIENT_IP = get_client_ip() -- 获取客户端ip
local BLACK_IP_FILE = get_rule("black_ip.txt") -- 获取黑名单文件
if BLACK_IP_FILE ~= nil then -- 如果黑名单文件不为nil
if config_black_ip_check == "on" then
for _, rule in ipairs(BLACK_IP_FILE) do
if CLIENT_IP == rule then -- 如果客户端ip为黑名单
log_record('BlackList_IP',ngx.var_request_uri,"_","_")
if config_waf_enable == "on" then -- 判断是否开启waf防护
ngx.exit(403) -- 请求结束,返回403
return true -- 返回true
end
end
end
end
end
end
end
function sql_injection()
if config_sql_injection == "on" then
local method = ngx.req.get_method()
if method == "POST" then -- 如果是post请求
ngx.say(method)
post_sql()
else -- get请求
ngx.say(method)
get_sql()
end
end
end
function cc_defense()
if config_cc_check == "on" then
local rds,err = require("resty.redis")
rds:set_timeout(2000)
local CLIENT_IP = get_client_ip()
local redis = rds:new()
local ok,err = redis:connect("192.168.217.144",6379)
if not ok then
ngx.log(ngx.ERR,"failed to connect redis: ",err)
ngx.say("failed to connect redis: ",err)
return
end
local value = redis:get(CLIENT_IP) -- 获取redis中该客户端ip的值
if value == ngx.null then -- 如果客户端ip的值为nil
redis:set(CLIENT_IP,1)
elseif value == "60" then -- 如果1分钟内访问次数达到60,则将ip假如黑名单
ngx.say(CLIENT_IP,"black_ip")
file=io.open("/application/conf/waf/black_ip.txt","a")
io.output(file)-- 设置默认输出文件
io.write(CLIENT_IP.."\n")
io.close()
return
else
redis:set(CLIENT_IP,value + 1)
end
end
end
lib文件中主要是一些工具方法。
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by Administrator.
--- DateTime: 2022/8/20 16:03
---
require("config")
-- 获取client ip
function get_client_ip()
CLIENT_IP = ngx.req.get_headers()["X_real_ip"]
if CLIENT_IP == nil then
CLIENT_IP = ngx.req.get_headers()["X_Forwarded_For"]
end
if CLIENT_IP == nil then
CLIENT_IP = ngx.var.remote_addr
end
if CLIENT_IP == nil then
CLIENT_IP = "unknown"
end
return CLIENT_IP
end
-- 获取文件
function get_rule(rulefilename)
local io = require("io") --引入IO
local RULE_PATH = config_rule_dir -- 获取配置文件路径
local RULE_FILE = io.open(RULE_PATH..'/'..rulefilename,"r")
if RULE_FILE == nil then
return
end
RULE_TABLE = {}
for line in RULE_FILE:lines() do
print(line)
table.insert(RULE_TABLE,line)
end
RULE_FILE:close()
return(RULE_TABLE)
end
-- 记录日志
function log_record(method,url,data,ruletag)
local cjson = require("cjson")
local io = require 'io'
local LOG_PATH = config_log_dir
local CLIENT_IP = get_client_ip()
local SERVER_NAME = ngx.var.server_name
local LOCAL_TIME = ngx.localtime()
local log_json_obj = {
client_ip = CLIENT_IP,
local_time = LOCAL_TIME,
server_name = SERVER_NAME,
user_agent = USER_AGENT,
attack_method = method,
req_url = url,
req_data = data,
rule_tag = ruletag,
}
local LOG_LINE = cjson.encode(log_json_obj)
local LOG_NAME = LOG_PATH..'/'..ngx.today().."_waf.log"
local file = io.open(LOG_NAME,"a")
if file == nil then
return
end
file:write(LOG_LINE.."\n")
file:flush()
file:close()
end
-- get请求下如何处理sql注入
function get_sql()
local arg = ngx.req.get_uri_args()
for k,v in pairs(arg) do
s = k
x = v
key = k..v
ngx.say(k..":"..v)
ngx.say(ngx.quote_sql_str(v))
end
end
-- post请求下如何处理sql注入
function post_sql()
local http = require("resty.http")
ngx.req.read_body()
local post_args = ngx.req.get_post_args()
for k, v in pairs(post_args) do
local value = ngx.quote_sql_str(v)
ngx.say(value)
local regex = "(.*?((union)|(insert)|(drop)|(truncate)|(update)|(from)|(grant)|(exec)|(where)|(select)|(chr)|(mid)|(like)|(iframe)|(script)|(alert)|(webscan)|(dbappsecurity)|(style)|(WAITFOR)|(confirm)|(innerhtml)|(innertext)|(class)).*?){1,}"
local m = ngx.re.match(value, regex)
if m then
ngx.say('{"code": 999,"msg": "非法参数","ok": false,"runningTime": "0ms"}')
end
end
end
config.lua中是配置文件,配置各个功能点是否开启
--WAF config file,enable = "on",disable = "off"
--waf status
config_waf_enable = "on"
--log dir
config_log_dir = "/waflogs"
--rule setting
config_rule_dir = "/application/conf/waf"
--enable/disable white url
config_white_url_check = "on"
--enable/disable white ip
config_white_ip_check = "on"
--enable/disable block ip
config_black_ip_check = "on"
--enable/disable url filtering
config_url_check = "on"
--enalbe/disable url args filtering
config_url_args_check = "on"
--enable/disable user agent filtering
config_user_agent_check = "on"
--enable/disable cookie deny filtering
config_cookie_check = "on"
--enable/disable cc filtering
config_cc_check = "on"
--cc rate the xxx of xxx seconds
config_cc_rate = "10/60"
--enable/disable post filtering
config_post_check = "on"
--config waf output redirect/html
config_sql_injection = "on"
config_cc_defens = "on"
config_waf_output = "html"
--if config_waf_output ,setting url
config_waf_redirect_url = "https://www.unixhot.com"
config_output_html=[[
OpsAny|Web应用防火墙
检测入侵,请求终止
]]
白名单配置文件
192.168.217.1
黑名单配置文件
192.168.217.102