OpenResty+Lua实现WAF防火墙

OpenResty+Lua实现WAF防火墙

WAF功能

  • 白名单
  • 黑名单
  • 防止SQL注入
  • 防止CC攻击

若服务器收到CC攻击,一分钟内请求数大于60,则会将对方ip封进黑名单

文件结构

  • waf 项目文件夹
    • init.lua 初始化函数
    • access.lua 请求入口处
    • lab.lua 工具方法如获取客户端ip,防止GET/POST请求的SQL注入等。
    • config.lua 配置文件
    • white_ip.txt 白名单
    • black_ip.txt 黑名单

项目演示

防止sql注入

  1. 过滤select、update、insert、delete等sql语法
  2. 过滤如’or ''1 = 1’这类字符串
过滤sql语句GET

使用浏览器发送GET请求,在路径中拼接drop database xxx等sql语句

OpenResty+Lua实现WAF防火墙_第1张图片

过滤sql语句POST

使用apifox发送post请求,在路径中拼接drop database xxx 等sql语句

OpenResty+Lua实现WAF防火墙_第2张图片

过滤 or 1=1 类型语句 GET

OpenResty+Lua实现WAF防火墙_第3张图片

获取GET请求中的参数,将’'字符串转译成’/‘即可防止该SQL注入

过滤or 1=1 类型语句 POST

OpenResty+Lua实现WAF防火墙_第4张图片

获取POST请求体中的字符串,将’‘转译成’/’

GET和POST请求防止SQL注入测试成功

防止CC攻击

客户端正常访问请求能够成功响应

OpenResty+Lua实现WAF防火墙_第5张图片

查看redis中该ip请求次数
OpenResty+Lua实现WAF防火墙_第6张图片

查看黑名单

OpenResty+Lua实现WAF防火墙_第7张图片

如果1分钟内该ip请求数超过50则假如黑名单。接下来使用金额jemter工具进行压测接口

OpenResty+Lua实现WAF防火墙_第8张图片

发送60个请求可以看到waf将客户端ip添加进了黑名单
OpenResty+Lua实现WAF防火墙_第9张图片

当我再次访问,则直接提示403
OpenResty+Lua实现WAF防火墙_第10张图片

实现方法

配置NGINX

# 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;
	
	   }
	
    }


}

access.lua文件

---
--- 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.lua文件

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文件

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文件

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应用防火墙


检测入侵,请求终止 ]]

white_ip.txt

白名单配置文件

192.168.217.1

black_ip.txt

黑名单配置文件

192.168.217.102

你可能感兴趣的:(openresty,Lua,lua,openresty,开发语言)