OpenResty通过Lua脚本在请求处理阶段实时执行安全规则,结合shared_dict计数、kafka异步投递,实现“边采集边计算”,在毫秒级完成恶意请求识别、拦截、打标、分发闭环,避免传统日志后分析的延迟缺陷。

OpenResty 本身不直接做“计算”,但通过 Lua 脚本与 Kafka、共享字典(shared_dict)、定时器(ngx.timer)和请求上下文的深度结合,可以实现在日志采集链路中同步完成轻量级安全判断与响应——即“边采集边计算”。这不是替代 Flink/Spark 的流式计算,而是把关键防御逻辑前移到请求入口,在毫秒级内完成识别、拦截、标记、投递四步闭环。
核心能力:在日志写入前完成实时决策
传统日志采集是“先记录后分析”,而 OpenResty 的优势在于:它在 请求处理阶段 就能拿到完整上下文(URI、Header、Body、IP、UA、耗时、Referer 等),并用 Lua 实时执行规则。这意味着:
- 恶意请求可被 0 延迟拦截(如 SQL 注入、高频爬虫),无需等日志落盘再触发告警
- 可疑行为可被 打标后投递(如加 X-Abnormal: "sql-inj" 头),Kafka 消费端按需分流处理
- 高频攻击源可被 动态限流或封禁(基于 shared_dict 计数,无需依赖外部存储)
- 灰度策略可与日志采集联动(如只对 v2.1 流量启用 XSS 检测,并单独投递到 kafka-topic-xss)
典型架构:Nginx → OpenResty(Lua)→ Kafka + shared_dict
整个流程不经过磁盘日志文件,全部在内存中完成:
- 采集层:OpenResty 在 log_by_lua* 阶段读取请求上下文,构造结构化 JSON(含时间戳、IP、UA、匹配规则名、响应码等)
- 计算层:调用预加载的 Lua 规则模块(如检测 UA 是否含 HeadlessChrome、Referer 是否为空、URI 是否含 union/select)
- 决策层:根据规则结果设置变量(如 is_malicious = true, threat_level = "high"),并写入 shared_dict 做 IP 统计
- 投递层:使用 lua-resty-kafka 将带标签的事件异步推送到 Kafka 不同 topic(如 security-alert、behavior-log、debug-trace)
实战配置片段(关键逻辑示意)
以下为 nginx.conf 中 server 块内关键 Lua 片段,已精简去除非核心内容:
# 共享字典用于限流和统计
lua_shared_dict ip_threat 10m;
<p>log_by_lua_block {
local json = require "cjson"
local kafka = require "resty.kafka.producer"</p><pre class="brush:php;toolbar:false;">-- 1. 提取基础字段
local ip = ngx.var.remote_addr
local ua = ngx.var.http_user_agent or ""
local uri = ngx.var.request_uri
local status = tonumber(ngx.var.status) or 0
-- 2. 轻量级规则匹配(避免正则爆炸)
local is_bot = ua:match("HeadlessChrome") or ua:match("Scrapy") or (ua == "")
local is_scan = uri:match("/api/v1/%d+/") and status == 404
-- 3. 动态计数(5分钟窗口)
local dict = ngx.shared.ip_threat
local key = "threat:" .. ip
local val, err = dict:incr(key, 1, 0)
if not val then
dict:set(key, 1, 300) -- 过期 5 分钟
end
-- 4. 判定是否高危并投递
if is_bot or is_scan or (val and val > 20) then
local event = {
ts = ngx.now(),
ip = ip,
ua = ua,
uri = uri,
status = status,
rule_hit = is_bot and "ua-bot" or is_scan and "path-scan" or "ip-flood",
count = val
}
-- 异步发往安全告警 topic
local ok, err = kafka:new():send("security-alert", nil, json.encode(event))
if not ok then
ngx.log(ngx.ERR, "kafka send failed: ", err)
end
-- 同时触发拦截动作(如返回 403 或重定向)
if is_bot or is_scan then
ngx.exit(403)
end
end
}
为什么这比“先记日志再分析”更有效?
因为真正的攻击防御必须发生在请求未造成影响之前。例如:
- 一个每秒 200 次的 /login 接口爆破,若等 ELK 收集完日志再触发告警,账号可能已被撞库成功;而 OpenResty 可在第 11 次请求就限流,第 21 次直接 403
- 某次 XSS 攻击携带
<script>fetch('/api/token')</script>,OpenResty 在 access_by_lua 阶段即可匹配并阻断,不会让恶意 payload 进入后端或被记录为“正常访问日志” - 爬虫绕过 CDN 直连源站时,OpenResty 可结合 X-Internal-Token 校验 + UA + Referer 三重判断,拒绝非法流量,同时将该 IP 加入 shared_dict 黑名单,后续请求自动拦截










