详解针对Websocket协议的帧检查算法与长连接恶意消耗防御
摘要:# 当攻击者盯上你的“聊天室”:Websocket长连接,如何防住那些“赖着不走”的恶意流量? 前几天,一个做在线游戏的朋友半夜给我打电话,语气快崩溃了:“我们新上的实时对战功能,服务器CPU直接飙到100%,但看带宽又没异常。玩家全卡掉了,这到底什么路…
当攻击者盯上你的“聊天室”:Websocket长连接,如何防住那些“赖着不走”的恶意流量?
前几天,一个做在线游戏的朋友半夜给我打电话,语气快崩溃了:“我们新上的实时对战功能,服务器CPU直接飙到100%,但看带宽又没异常。玩家全卡掉了,这到底什么路数?”
我让他把日志发过来一看,好家伙,满屏都是Websocket连接,建立起来之后,就隔几十秒发个几字节的“心跳包”,不干正事,光占着茅坑不拉屎。服务器资源(内存、文件描述符)被这些“僵尸连接”一点点啃食殆尽。这就是典型的针对Websocket长连接的恶意消耗攻击,也叫慢速攻击的一种变体。
很多刚上Websocket实时业务(像弹幕、协同编辑、在线客服、金融行情)的团队,以为上了HTTPS、配了普通WAF就高枕无忧了。结果真遇到这种“非主流”攻击,传统的基于HTTP请求速率和包大小的防护规则,基本就瞎了。因为Websocket走的是升级后的长连接,攻击藏在合法的协议交互里。
今天咱就掰开揉碎了讲,怎么对付这种阴招。核心就两点:看懂帧(Frame Inspection),以及管好连接(Connection Management)。不扯虚的,直接上干货。
一、 Websocket攻击:为啥传统防护“看不见”?
首先得明白,Websocket握手成功后,后续数据传输用的就是数据帧(Data Frames),不再是HTTP的请求/响应模型。攻击者就钻这个空子:
- 慢速消耗型:建立连接后,以极低的速率(比如每分钟)发送一个微小的数据帧(甚至是不完整的帧)。服务器为了维持这个连接,得一直分配资源等着,几千上万个这样的连接,就能把服务器拖垮。这感觉就像——电话接通了,对方也不挂,就隔半天对你喘口气,你啥也干不了,还不能先挂。
- 畸形帧攻击:发送违反RFC6455协议规范的畸形帧,比如错误的分片(Fragmentation)、超大的负载长度、错误的操作码(Opcode)。一些实现不严谨的服务器库可能直接崩溃,或者进入高消耗的异常处理逻辑。
- 资源耗尽型:快速建立大量Websocket连接,占满服务器的文件描述符和内存,让正常用户连不上。
传统WAF或防火墙的盲区:它们大多深度解析到HTTP握手层就结束了。握手成功后,后面传输的Websocket数据帧,对它们来说就是普通的TCP流量,看不懂内容,也就无从判断某个长连接里传输的“心跳”是正常的业务心跳,还是恶意的心跳。
所以,防御的第一道门槛,就是你得能看懂Websocket帧。
二、 核心防御:帧检查算法,到底在查什么?
所谓“帧检查”,就是像安检机一样,对Websocket数据帧的头部(Header)和负载(Payload) 进行实时解析和规则匹配。这不是简单看个大小,而是理解其语义。关键检查点如下:
1. 帧头合规性检查(基础防线) 这是最低成本的过滤。任何不符合RFC6455标准的帧,都应该在入口被直接丢弃或关闭连接。主要查:
- 操作码(Opcode):是不是0x0-0x2,0x8-0xA这些有效值?突然来个0x5?直接拒掉。
- 分片(FIN, RSV1-3):检查分片控制位是否被非法使用。比如,非控制帧的RSV位必须为0(除非协商了扩展)。很多攻击会乱设这些保留位来试探服务器漏洞。
- 负载长度:检查声明的负载长度和实际传输的长度是否匹配。声明巨大但实际传一点点,或者反过来,都可能是攻击探针。
- 掩码(Masking):从客户端发往服务器的帧,必须掩码。没掩码?按照协议,可以直接断开连接。这是协议规定的,不是可选项。
(说真的,很多开源库的默认配置为了兼容性,对这些检查并不严格,这就给攻击者留了后门。生产环境一定要调严。)
2. 业务语义速率控制(关键所在) 这才是区分正常用户和攻击者的核心。你需要定义业务层面的“正常行为”模型。
- 心跳帧间隔与频率:你们的业务正常心跳是30秒一次?那如果某个连接每2秒就发一个Ping帧,或者长达10分钟不发任何数据(可能是慢速攻击),都可以被标记为异常。
- 消息速率与大小:一个实时聊天室,正常用户每秒发几条消息?每条消息多大?如果一个连接每秒狂发上百条小消息(可能是刷屏脚本),或者持续发送巨大单帧(试图耗尽后端处理资源),就需要限制。
- 请求/响应模式:对于某些业务,如游戏指令,可能存在“客户端发送一个动作帧,必须等待服务器回应后再发下一个”的逻辑。如果客户端不守规矩,疯狂发送,就需要干预。
实现上,这通常需要在网关或防护设备上为每个Websocket连接维护一个会话状态,记录其最近的活动频率、消息量等,并应用对应的令牌桶(Token Bucket)或漏桶(Leaky Bucket)算法进行限速。难点在于,这个“正常阈值”需要根据你的实际业务来调,没有放之四海而皆准的值。
3. 负载内容深度检测(Deeper Inspection) 即使帧头合规、速率正常,负载里也可能有鬼。这需要将Websocket帧的负载部分(解密后)提取出来,进行下一步分析:
- 如果是JSON/文本:可以像防HTTP API攻击一样,检查是否有SQL注入、XSS、敏感信息泄露等特征。
- 如果是二进制协议(如游戏):需要与业务逻辑深度结合,解析具体指令字段,判断其合法性。比如,一个“移动”指令的坐标值是否超出了地图边界?一个“购买”指令的物品ID是否存在?
- 这步成本较高,通常用于对业务最关键、风险最高的指令。
三、 长连接管理:给连接设“规矩”与“寿命”
光检查帧不够,还得从连接生命周期层面进行管控。
1. 强制超时与保活 这是对付“慢速消耗”的杀手锏。必须给连接设置多层超时:
- 握手超时:从TCP连接到完成Websocket握手的时间,宜短不宜长(如3-5秒)。
- 非活动超时:在连接建立后,如果超过一段时间(比如60秒)没有收到任何有效数据帧(注意,是能通过帧检查的帧),就主动关闭连接。这让那些“只握手、不干活”或“慢速发心跳”的攻击连接活不长。
- 绝对生存时间:即使连接活跃,也设置一个最大持续时间(如2小时)。到点就优雅地要求重连,防止内存泄漏,也强制刷新客户端状态。
2. 连接数限制与来源封禁
- 单IP/用户连接数限制:一个正常用户,同时建立几个Websocket连接?通常1-3个顶天了。如果一个IP瞬间建立上百个连接,可以直接封禁该IP一段时间。
- 全局连接数配额:服务器或代理层要有硬性的总连接数限制,避免资源彻底耗尽。
- 结合行为封禁:如果某个连接因发送畸形帧或超速被关闭,可以将其IP或Session ID加入短期黑名单,防止其快速重连。
3. 优雅降级与资源隔离 对于重要业务,可以考虑:
- 将Websocket服务与核心API服务部署隔离,避免Websocket被攻击时拖垮整个网站。
- 设置资源池,当检测到攻击导致连接数激增时,新连接可以进入一个“低优先级队列”或返回一个“系统繁忙”的友好提示,保证已连接的老用户不受影响。
四、 实战配置思路(说点干的)
理论说完,落地咋办?分几种情况:
-
如果你用云服务(阿里云、腾讯云、AWS等): 赶紧去看看你的云WAF或高防IP控制台。现在主流云厂商的WAF产品,大多已经提供了“Websocket防护”的开关或独立模块。你需要做的,就是打开它,然后仔细配置里面的“帧检查规则”和“连接超时”参数。别用默认值!根据你的业务流量基线(平时高峰期的连接数、消息频率)来调整。这是最快、最省心的办法。
-
如果你自建防护(用Nginx、Envoy等网关): 这需要更多功夫。以Nginx为例(>=1.3.13支持Websocket代理),它主要是个代理,深层防护能力有限。你需要:
- 在
location里用proxy_read_timeout设置非活动超时(这个很关键)。 - 用
limit_conn模块限制单IP连接数。 - 更复杂的帧检查和速率限制,需要结合OpenResty(Nginx+Lua) 写脚本自己解析Websocket帧头,或者使用专门的模块(如
nchan)。这对团队技术要求高。
- 在
-
如果你有实力自研中间件: 可以考虑在业务代码的Websocket处理层(如使用
ws库的Node.js,gorilla/websocket的Go)直接集成上述检查逻辑。比如,在onMessage回调里,先检查消息频率,再处理业务。这样控制最精细,但维护成本也最高。
最后几句大实话
防护Websocket攻击,没有一劳永逸的银弹。它本质上是在业务体验和安全严格性之间找平衡。
- 先监控,再防护:上线前,用
netstat、ss命令,或者监控系统的连接数、帧速率图表,摸清你们业务的正常基线。不知道什么是正常,就永远发现不了异常。 - 从简到繁:先上最容易的连接超时和单IP限连,能挡掉大部分低水平扫描和脚本攻击。再逐步细化帧检查和业务规则。
- 做好压力测试:上线前,自己用工具(如
gatling、ws的autobahn测试套件)模拟一下慢速连接和畸形帧攻击,看看服务会不会跪。别等真被打的时候才当测试环境。 - 别指望一套规则吃遍天:今天防住了慢速消耗,明天攻击者可能改用快速发送合法小消息来消耗你CPU。防护策略得持续迭代,跟业务一起成长。
说白了,Websocket防护,就是给这条长期开放的“数据通道”加上智能的安检员和巡逻队。既不能把每个客人都当贼一样搜身(影响体验),也不能大门敞开谁都能进(等着被冲垮)。
你的实时业务上线时,这块想好怎么做了吗?如果还没,现在可能就是最好的时机。

