Nginx 通过 ngx_lua_module 在 Upgrade 阶段校验 WebSocket 握手请求,检查头部、URI、参数、IP 行为及 Origin、Sec-WebSocket-Key 等,非法则直接拦截,避免透传后端。

直接在 Nginx 层拦截非法 WebSocket 连接,核心是利用 ngx_lua_module 在请求升级(Upgrade)阶段介入,对 Upgrade: websocket 和 Connection: upgrade 头、URI、参数、IP 行为等做实时校验,不放行即终止,避免透传到后端服务。
识别并拦截非法 WebSocket 升级请求
WebSocket 连接始于 HTTP 的 GET 请求 + 特定头部。Nginx 默认只做代理转发,需在 location 块中用 Lua 拦截该初始握手请求:
- 检查
ngx.var.http_upgrade是否为websocket,且ngx.var.http_connection包含upgrade,否则直接拒绝(非 WebSocket 请求不应进此逻辑) - 验证
ngx.var.uri是否匹配合法路径(如/ws/或/api/v1/ws),禁止通配或敏感路径(如/admin/ws) - 提取并校验查询参数:例如要求携带
token=xxx,用ngx.req.get_uri_args()获取后做签名验证或白名单比对
基于 IP 和行为的动态访问控制
仅靠路径和参数不够,还需结合客户端行为做动态决策:
- 使用
lua_shared_dict(如ws_attacks)记录每个 IP 在最近 60 秒内发起的 WebSocket 握手次数,超阈值(如 ≥5 次/分)立即返回429 Too Many Requests - 将高频、无 token、User-Agent 异常(如空值、
curl/7.0、含sqlmap字样)的 IP 自动写入共享字典黑名单,后续请求直接ngx.exit(403) - 可选对接 Redis 实时查 IP 黑白名单,但需注意 Lua 调用 Redis 是阻塞操作,建议用
resty.redis+ 连接池,并设超时(set_timeout(200))
防止伪造与协议滥用
攻击者可能伪造 WebSocket 请求头或混用协议,需针对性过滤:
- 拒绝
Origin头为空或来自不可信域名(如非业务域名、localhost、127.0.0.1)的请求,尤其在生产环境 - 检查
Sec-WebSocket-Key是否符合 Base64 格式(正则^[A-Za-z0-9+/]*={0,2}$),非法值直接拦截 - 禁止 URI 中含典型攻击特征:如
%3Cscript%3E(<script></script>)、union select、javascript:等,复用 WAF 规则库中的 pattern 匹配逻辑
配置示例与生效要点
在 nginx.conf 的对应 location /ws/ { ... } 块中嵌入:
- 前置执行
access_by_lua_file /path/to/ws_guard.lua;(非content_by_lua,因握手阶段尚未进入内容处理) - 确保
http块已声明:lua_shared_dict ws_limits 10m;和lua_package_path "/path/to/lua/?.lua;;"; - 重载配置前验证语法:
nginx -t;重启后用curl -i -H "Upgrade: websocket" -H "Connection: upgrade" http://host/ws/?token=xxx测试拦截效果











