目录
- OpenResty 与 WAF 的契合点
- OpenResty 的 Lua 处理阶段概览
- init_by_lua*:全局初始化
- init_worker_by_lua*:Worker 进程初始化
- ssl_certificate_by_lua*:动态 SSL 证书处理
- set_by_lua*:设置 Nginx 变量
- rewrite_by_lua*:URL 重写与重定向检查
- access_by_lua*:访问控制与认证
- content_by_lua*:内容生成与动态防护
- header_filter_by_lua*:响应头处理
- body_filter_by_lua*:响应体过滤
- log_by_lua*:日志记录与分析
- balancer_by_lua*:负载均衡安全策略
- stream_by_lua*:非 HTTP 流防护
- timer_by_lua*:定时任务防护
- 部署与优化建议
- 总结
OpenResty 与 WAF 的契合点
OpenResty 的核心优势在于其非阻塞、事件驱动的架构,结合 LuaJIT 的高性能脚本执行能力,能够在高并发场景下高效处理请求。对于 WAF 来说,这种架构意味着可以在每个请求的处理阶段插入安全检查逻辑,而不会显著增加延迟。此外,OpenResty 提供了丰富的 Lua API(如 ngx.var
、ngx.req
、ngx.resp
),可以轻松访问和修改请求/响应的各种属性,非常适合实现复杂的防护规则。
为什么选择 OpenResty 构建 WAF?
- 高性能:基于 Nginx 的事件驱动模型,单进程可处理数万并发请求。
- 灵活性:Lua 脚本允许动态配置规则,无需重启服务。
- 轻量级:LuaJIT 占用内存少,执行速度快。
- 模块化:支持 Redis、Memcached 等外部存储,适合分布式 WAF 部署。
- 社区支持:OpenResty 拥有活跃的社区和丰富的 Lua 模块(如
lua-resty-waf
)。
接下来,我们将逐一解析 OpenResty 的 Lua 处理阶段,探讨如何在每个阶段实现 WAF 功能。
OpenResty 的 Lua 处理阶段概览
OpenResty 将 HTTP 请求处理分为多个阶段,每个阶段都有特定的作用。通过在这些阶段注入 Lua 脚本,我们可以实现从初始化到日志记录的全面安全防护。以下是 HTTP 请求的阶段顺序(不包括非请求阶段):
ssl_certificate_by_lua*
(若启用 SSL/TLS)set_by_lua*
rewrite_by_lua*
access_by_lua*
content_by_lua*
header_filter_by_lua*
body_filter_by_lua*
log_by_lua*
balancer_by_lua*
此外,还有非请求阶段(如 init_by_lua*
、init_worker_by_lua*
、timer_by_lua*
)和流模块阶段(如 stream_by_lua*
)。我们将逐一讲解每个阶段的 WAF 应用。
init_by_lua*:全局初始化
作用与原理
init_by_lua*
在 Nginx 主进程启动时执行,仅运行一次,用于全局配置的初始化。WAF 可以在此阶段加载黑白名单、规则库或连接外部存储(如 Redis)。
- 适用场景:初始化全局共享数据(如 IP 黑名单、恶意 User-Agent 列表)。
- 注意事项:此阶段运行在主进程,需避免阻塞操作。
示例:加载 IP 黑名单
假设我们维护一个 IP 黑名单文件 /etc/nginx/blacklist.txt
,在 init_by_lua*
中加载到全局 Lua 表中。
|
|
代码说明:
- 使用
_G
创建全局表blacklist
,存储黑名单 IP。 - 读取
/etc/nginx/blacklist.txt
,每行一个 IP 地址。 - 后续阶段可通过
_G.blacklist[ip]
检查 IP 是否在黑名单中。
init_worker_by_lua*:Worker 进程初始化
作用与原理
init_worker_by_lua*
在每个 Nginx Worker 进程启动时执行,用于 Worker 级别的初始化。WAF 可以在此阶段为每个 Worker 初始化本地缓存或计数器。
- 适用场景:初始化 Worker 级别的限流计数器或临时黑名单。
- 注意事项:每个 Worker 独立运行,数据不共享。
示例:初始化请求限流计数器
为每个 Worker 初始化一个简单的请求计数器,用于后续限流。
|
|
代码说明:
- 使用
ngx.shared
创建共享内存字典counter
,在 Worker 间共享。 - 后续可在
access_by_lua*
中使用此计数器实现限流。
ssl_certificate_by_lua*:动态 SSL 证书处理
作用与原理
ssl_certificate_by_lua*
在 SSL/TLS 握手阶段执行,用于动态选择或配置 SSL 证书。WAF 可以在此阶段检查客户端证书或 TLS 扩展(如 SNI)。
- 适用场景:检测可疑的 TLS 客户端证书或 SNI 伪装。
- 注意事项:需启用 SSL 模块,且此阶段对性能敏感。
示例:检查 SNI 合法性
检查客户端请求的 SNI 是否在允许的域名列表中。
|
|
代码说明:
- 使用
ngx.ssl.server_name()
获取 SNI。 - 检查 SNI 是否在白名单中,否则终止连接。
set_by_lua*:设置 Nginx 变量
作用与原理
set_by_lua*
用于设置 Nginx 变量,常用于请求处理早期设置标志或参数。WAF 可以在此阶段为请求标记风险等级。
- 适用场景:为请求设置安全评分或标记。
- 注意事项:适合轻量逻辑,避免复杂计算。
示例:标记高风险 User-Agent
为包含特定 User-Agent 的请求设置高风险标志。
|
|
代码说明:
- 检查
http_user_agent
是否包含 “bot” 或 “spider”。 - 设置
ngx.var.risk_level
变量,供后续阶段使用。
rewrite_by_lua*:URL 重写与重定向检查
作用与原理
rewrite_by_lua*
在 URL 重写阶段执行,用于修改请求 URI 或执行重定向。WAF 可以在此阶段检测恶意的 URI 模式(如 SQL 注入)。
- 适用场景:阻止恶意 URI 或重定向可疑请求。
- 注意事项:可触发内部重定向,需避免循环。
示例:检测 SQL 注入模式
检查 URI 是否包含 SQL 注入关键字。
|
|
代码说明:
- 检查 URI 是否匹配 SQL 注入模式(如
union select
)。 - 若检测到攻击,重定向到 403 页面。
access_by_lua*:访问控制与认证
作用与原理
access_by_lua*
在访问控制阶段执行,用于认证和授权检查。WAF 可以在此阶段实现 IP 黑名单检查、限流或令牌验证。
- 适用场景:阻止黑名单 IP、限制请求速率。
- 注意事项:常用于终止请求,需快速执行。
示例:IP 黑名单与限流
结合 init_by_lua*
的黑名单和 init_worker_by_lua*
的计数器,实现 IP 黑名单检查和限流。
|
|
代码说明:
- 检查 IP 是否在
_G.blacklist
中,若是则返回 403。 - 使用共享内存实现每分钟 100 次请求的限流,超限返回 429。
content_by_lua*:内容生成与动态防护
作用与原理
content_by_lua*
在内容生成阶段执行,用于生成响应内容。WAF 可以在此阶段动态生成错误页面或检查 POST 请求体。
- 适用场景:处理复杂请求体检查或自定义错误页面。
- 注意事项:适合复杂逻辑,但需注意性能。
示例:检查 POST 请求体
检测 POST 请求体中的 XSS 攻击。
|
|
代码说明:
- 检查 POST 请求体是否包含
<script
标签。 - 若检测到 XSS,返回 403 并显示错误信息。
header_filter_by_lua*:响应头处理
作用与原理
header_filter_by_lua*
在响应头处理阶段执行,用于修改或添加响应头。WAF 可以在此阶段添加安全头(如 CSP、X-XSS-Protection)。
- 适用场景:增强响应安全性,防止客户端攻击。
- 注意事项:仅处理头信息,执行快速。
示例:添加安全响应头
为所有响应添加常见的安全头。
|
|
代码说明:
- 添加 CSP、XSS 防护和防点击劫持的响应头。
- 提升客户端浏览器的安全性。
body_filter_by_lua*:响应体过滤
作用与原理
body_filter_by_lua*
在响应体处理阶段执行,用于修改或过滤响应内容。WAF 可以在此阶段替换敏感数据或检测泄露信息。
- 适用场景:过滤响应中的敏感信息(如信用卡号)。
- 注意事项:可能多次调用,需高效处理。
示例:过滤信用卡号
将响应体中的信用卡号替换为星号。
|
|
代码说明:
- 使用正则表达式匹配 16 位信用卡号。
- 将匹配内容替换为
****-****-****-****
。
log_by_lua*:日志记录与分析
作用与原理
log_by_lua*
在日志记录阶段执行,用于自定义日志或发送警报。WAF 可以在此阶段记录攻击事件或发送到外部系统(如 ELK)。
- 适用场景:记录可疑请求,生成安全报告。
- 注意事项:异步执行,不影响响应。
示例:记录可疑请求
将高风险请求记录到单独日志文件。
|
|
代码说明:
- 检查
risk_level
是否为 “high”。 - 将请求信息写入
/var/log/nginx/waf.log
。
balancer_by_lua*:负载均衡安全策略
作用与原理
balancer_by_lua*
在上游负载均衡阶段执行,用于自定义后端服务器选择。WAF 可以在此阶段实现基于安全的负载均衡策略。
- 适用场景:隔离可疑请求到特定后端。
- 注意事项:需配置上游服务器。
示例:隔离高风险请求
将高风险请求路由到隔离服务器。
|
|
代码说明:
- 检查
risk_level
,若为 “high”,路由到隔离服务器(127.0.0.1:8081)。 - 需在 Nginx 配置中定义上游服务器。
stream_by_lua*:非 HTTP 流防护
作用与原理
stream_by_lua*
用于处理 TCP/UDP 流量,适用于非 HTTP 协议。WAF 可以在此阶段实现端口级别的防护(如阻止恶意 SSH 连接)。
- 适用场景:保护数据库或邮件服务器。
- 注意事项:需启用流模块。
示例:阻止黑名单 IP 的 TCP 连接
阻止黑名单 IP 访问数据库端口。
|
|
代码说明:
- 检查 TCP 连接的客户端 IP 是否在黑名单中。
- 若在黑名单,终止连接。
timer_by_lua*:定时任务防护
作用与原理
timer_by_lua*
用于运行后台定时任务,WAF 可以在此阶段更新黑名单或清理缓存。
- 适用场景:动态更新规则或清理过期数据。
- 注意事项:异步执行,避免阻塞。
示例:定期更新黑名单
每 5 分钟更新黑名单。
|
|
代码说明:
- 每 300 秒(5 分钟)重新加载黑名单。
- 使用
ngx.timer.at
实现循环任务。
部署与优化建议
部署步骤
- 安装 OpenResty:从 openresty.org 下载并编译,支持 Lua 和流模块。
- 配置 Nginx:在
nginx.conf
中添加 Lua 指令,引用上述脚本。 - 测试规则:使用
curl
或压测工具(如ab
)验证 WAF 效果。 - 监控日志:检查
/var/log/nginx/waf.log
和 Nginx 错误日志。
优化技巧
- 减少 I/O 操作:将黑名单缓存到 Redis 或共享内存。
- 异步日志:使用
ngx.timer
异步写入日志,避免阻塞。 - 规则优化:定期分析日志,精简匹配模式。
- 分布式部署:结合 Redis 实现集群化 WAF。
总结
通过 OpenResty 和 Lua,我们可以在请求处理的每个阶段实现灵活、高效的 Web 安全防护。从全局初始化到日志记录,每个阶段都有独特的用途,适合不同的防护需求。本教程提供的示例代码简单易懂,适合初学者快速上手,同时也为进阶用户提供了实用的参考。
希望这篇教程能帮助你构建自己的高性能 WAF!如果有任何问题,欢迎在博客评论区留言,我们一起探讨!
评论 0