去评论
dz插件网

服务器防CC攻击实战应用:精准控制每个IP请求:Nginx与OpenResty的攻防实战

婷姐
2025/08/24 09:47:13
娌℃湁闃茬嚎锛屽杽鎰忔祦閲忎細娣规病浣狅紱鐩茬洰闄愭祦锛岀湡瀹炵敤鎴峰皢绂讳綘鑰屽幓鈥斺€斾簰鑱旂綉鏈嶅姟鐨勭簿瀵嗗钩琛℃湳


姣忓綋閬囧埌鏈嶅姟鍣ㄧ獊鍙戦珮璐熻浇銆佹伓鎰忕埇铏柉鐙傛姄鍙栥€佹垨绔炰簤鑰呯殑CC鏀诲嚮锛岀簿鍑嗘帶鍒舵瘡涓狪P鐨勮姹傚氨鎴愪簡瀹堟姢鏈嶅姟鐨勬渶鍚庨槻绾裤€
鏈€杩戠爺绌朵簡涓€涓媙ginx鍜孫penResty鐨勯檺閫熻兘鍔涳紝鏈枃鎶婃秹鍙婂埌鐨勪竴浜涙妧鏈師鐞嗗拰閰嶇疆瑕佺偣璁板綍涓嬫潵銆
涓€銆丯ginx鍘熺敓闃叉帶锛氱涓€閬撻槻鐏


鏍稿績妯″潡锛

閰嶇疆绀轰緥锛
# 鍏ㄥ眬閰嶇疆 (http鍧)
limit_req_zone$binary_remote_addr zone=ip_req:10m rate=30r/s;
limit_conn_zone$binary_remote_addr zone=ip_conn:5m;

server {
location / {
    # 闄愰€燂細姣忕30璇锋眰+10绐佸彂锛坣odelay绔嬪嵆澶勭悊绐佸彂锛
    limit_req zone=ip_req burst=10 nodelay;
   
    # 姣忎釜IP鏈€澶20骞跺彂杩炴帴
    limit_conn ip_conn 20;
   
    # 灏佺瓒呴檺IP鏃惰繑鍥429鑰岄潪503
    error_page429 = @ratelimit;
  }

location@ratelimit {
    add_header Retry-After 10;
    return429'{"error": "Too Many Requests"}';
  }
}
鍏抽敭鍙傛暟璁$畻锛
浜屻€丱penResty鍔ㄦ€佹帶鍒讹細鏅鸿兘闃叉姢浣撶郴


褰撻渶瑕佸樊寮傚寲绛栫暐銆佽涓哄垎鏋愩€佸姩鎬佸皝绂佹椂锛屽師鐢烴ginx鍔涗笉浠庡績銆傚熀浜嶭ua鐨凮penResty鎻愪緵浜嗘棤闄愬彲鑳斤細
1. 瀹樻柟姝﹀櫒搴擄細lua-resty-limit-traffic

access_by_lua_block {
  local lim = require"resty.limit.req".new("ip_req_store", 100, 50)

-- 宸紓鍖栭檺閫燂細VIP鐢ㄦ埛鏀惧闄愬埗
local rate = is_vip(ip) and200or100
  lim:set_rate(rate)

if lim:incoming(ip) == "rejected"then
    ngx.header["Retry-After"] = 5
    return ngx.exit(429)
end
}2. 琛屼负鍒嗘瀽寮曟搸锛氳瘑鍒伓鎰忔ā寮

function detect_abnormal(ip)
  local dict = ngx.shared.ip_behavior
local key = ip..":pattern"

-- 缁熻10绉掑唴璇锋眰璺緞鐔靛€
local entropy = calculate_path_entropy(ip)

-- 鐔靛€煎紓甯稿崌楂樺垽瀹氫负鎵弿琛屼负
if entropy > THRESHOLD then
    dict:set(key, "scanner", 600)
    returntrue
end
end3. 鍒嗗竷寮忓皝绂佺郴缁燂紙Redis鐗堬級

local redis = require "resty.redis"
local red = redis:new()

-- 鑷姩鎷夐粦楂橀鏀诲嚮IP
if req_count(ip) > 1000 then
  red:sadd("global:blacklist", ip)
  red:expire(ip, 3600)
end

-- 鍏ㄥ眬榛戝悕鍗曟牎楠
if red:sismember("global:blacklist", ip) then
  ngx.exit(403)
end涓夈€佸灞傜骇闃插尽鏋舵瀯

           鈹屸攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹
           鈹 鍓嶇WAF闃叉姢    鈹  (Cloudflare/AWS WAF)
           鈹斺攢鈹€鈹€鈹€鈹€鈹€鈹攢鈹€鈹€鈹€鈹€鈹€鈹€鈹
                  鈹
           鈹屸攢鈹€鈹€鈹€鈹€鈹€鈻尖攢鈹€鈹€鈹€鈹€鈹€鈹  鍔ㄦ€佽鍒
           鈹 OpenResty   鈹溾攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹
           鈹 娣卞害琛屼负鍒嗘瀽 鈹             鈹
           鈹斺攢鈹€鈹€鈹€鈹€鈹€鈹攢鈹€鈹€鈹€鈹€鈹€鈹             鈹
                  鈹                    鈻
           鈹屸攢鈹€鈹€鈹€鈹€鈹€鈻尖攢鈹€鈹€鈹€鈹€鈹€鈹      鈹屸攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹
           鈹 Nginx       鈹      鈹  Redis闆嗙兢    鈹
           鈹 鍩虹闄愭祦     鈹傗梹鈹€鈹€鈹€鈹€鈹€鈻  鍒嗗竷寮忕姸鎬佸悓姝  鈹
           鈹斺攢鈹€鈹€鈹€鈹€鈹€鈹攢鈹€鈹€鈹€鈹€鈹€鈹      鈹斺攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹
                  鈹
           鈹屸攢鈹€鈹€鈹€鈹€鈹€鈻尖攢鈹€鈹€鈹€鈹€鈹€鈹
           鈹 Fail2ban     鈹 (鏃ュ織鍒嗘瀽鑷姩灏両P)
           鈹斺攢鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹鍥涖€佸疄鎴樹紭鍖栫瓥鐣

if is_static_file(uri) then
  rate = 1000  -- 闈欐€佽祫婧愭斁瀹
elseif uri == "/api/login" then
  rate = 2     -- 鐧诲綍鎺ュ彛涓ユ牸闄愬埗
endlocal geo = require "resty.maxminddb"
local is_cn = geo.lookup(ip).country == "CN"

-- 鍥藉唴IP鏀惧闄愬埗锛堥槻娴峰鏀诲嚮锛
lim:set_rate(is_cn and 200 or 50)# Python妯″瀷棰勬祴IP椋庨櫓鍊硷紙OpenResty閫氳繃gRPC璋冪敤锛
risk_score = model.predict(ip, request_pattern)
lua_rate = 1000 * (1 - risk_score)  # 楂橀闄㊣P鑷姩闄嶆潈缁撹锛氱簿搴︿笌鏁堢巼鐨勫钩琛¤壓鏈


娴侀噺鎺у埗濡傚悓璧伴挗涓濓細杩囦弗鍒欎激鍙婄湡瀹炵敤鎴凤紝杩囨澗鍒欐湇鍔″穿婧冦€侽penResty鎻愪緵浜嗗墠鎵€鏈湁鐨勭伒娲绘€э紝浣嗛渶璁颁綇锛


銆愰檮褰曪細閰嶇疆瑕佺偣閫熸煡琛ㄣ€
鍦烘櫙Nginx鍘熺敓鏂规OpenResty澧炲己鏂规
鍩虹閫熺巼闄愬埗limit_req_zonelua-resty-limit-traffic
璇锋眰鐗瑰緛鍒嗘瀽Lua瀹炴椂璁$畻璇锋眰鐔靛€
鍒嗗竷寮忕姸鎬佸悓姝Redis鍏变韩闆嗙兢鐘舵€
鍔ㄦ€佸皝绂鎵嬪姩Fail2ban鑷姩瀹炴椂榛戝悕鍗
鎱㈤€熸敾鍑婚槻鎶client_body_timeout姣绾ц姹傝秴鏃舵帶鍒