nginx+lua控制nginx异常访问

nginx+lua控制nginx异常访问

nginx+lua 防护,对异常请求进行计数并屏蔽

用 nginx+lua 对异常频率较大的IP请求进行限制;

原理:
将每次异常请求的IP记录下来,这个IP每次异常则计数+1;
如果3分钟内多于3次异常就屏蔽此IP 3分钟(从第1次开始计的时);
如果3分钟内异常多于9次,则延长至10分钟;

nginx.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
http {
    lua_shared_dict acl_ip_tables 1m;   # 开一个共享内存区域用来存储计数
    server {
        # 每个请求都会执行的逻辑
        access_by_lua_file /opt/app/nginx/conf/acl_req.lua;

        # 每个响应都会执行的逻辑
        header_filter_by_lua_file /opt/app/nginx/conf/acl_res.lua;

        location {}
    }
}

请求时逻辑 acl_req.lua

1
2
3
4
5
6
7
8
9
    -- 如果这个IP已经存在异常计数,且大于3次就拒绝访问
    local dict = ngx.shared.acl_ip_tables
    local ip = ngx.var.remote_addr          -- 获取请求使用的ip地址
    num = dict:get(ip)

    if ( num ~= nil ) and ( num > 2 )  then
        ngx.log(ngx.ERR, "acl_num >2: ", num, ' DROP 403 ')
        return ngx.exit(403)
    end

响应时逻辑 acl_res.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
-- 本次请求的响应码,如果不是 200/304 或特定响应码,视为异常

local status_code = ngx.var.status
local res_status = 0
if ( status_code == '200' ) or ( status_code == '304' ) then
    res_status = 1
elseif ( status_code == '999' ) then
    res_status = 1
end

if ( res_status == 0 ) then
    local ip = ngx.var.remote_addr      -- 获取请求使用的ip地址
    local dict = ngx.shared.acl_ip_tables
    num = dict:get(ip)

    if num == nil then
        -- 第一次异常访问
        ngx.log(ngx.ERR, "num: ",num, " status_code:", status_code," remote add: ", ngx.var.remote_addr)
        dict:set(ip, 1, 180)            -- 初始化计数
    else
        -- 存在异常时仍继续访问 +1
        num = dict:incr(ip, 1)
        ngx.log(ngx.ERR, "acl_num+1=", num)

        -- 3分钟内异常大于9次,延长至10分钟
        if ( num == 9 ) then
            dict:set(ip, 9, 600)
            ngx.log(ngx.ERR, "num: ",num, " set 10min: ", ip)
        end
    end
end

微信搜索IT运维小秋

Licensed under CC BY-NC-SA 4.0
转载或引用本文时请遵守许可协议,知会作者并注明出处
不得用于商业用途!
最后更新于 2022-01-28 00:00 UTC