面向公共互联网的 WebSocket 服务器是一个诱人的攻击目标。该协议没有内置的消息大小限制,因此单个连接可以尝试用一个精心构造的帧拖垮整个进程,而升级握手则是探测松散验证的一个容易下手的地方。最新的 sgcWebSockets 提供了一组默认关闭这些大门的保护机制,使你的服务器即使在遭受恶意流量时也能保持运行。
以下所有内容适用于全部三种 WebSocket 服务器:TsgcWebSocketServer、TsgcWebSocketHTTPServer 以及基于 http.sys 的 TsgcWebSocketServer_HTTPAPI。.NET 版本继承了相同的保护机制,因为它运行在相同的原生库之上。
一个限制阻止三种内存攻击
最重要的新增功能是一个单独的属性 MaxMessageSize,它限定了入站消息的最大大小。它默认为 64 MB,对几乎所有应用程序而言都很充裕,你可以调高或调低它,或将其设置为 0 以禁用该限制。
这一个属性可防御三种不同的内存耗尽技术,它们最终都以同样的方式收场,即服务器内存耗尽:
- 超大帧。客户端声明一个巨大的载荷长度。服务器现在会在开始读取帧主体之前拒绝该帧。
- 无尽分片。客户端打开一个分片消息但永不结束它,永久地流式发送续帧。服务器现在会限制所有分片重组后的总大小。
- 压缩炸弹。一个微小的 per-message-deflate 帧膨胀到数 GB。服务器现在会在膨胀输出达到限制的那一刻停止解压。
在每种情况下,连接都会以 WebSocket close code 1009(消息过大)干净地关闭,服务器的内存永远不会超过上限。
oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 80;
// accept messages up to 16 MB, reject anything larger with close 1009
oServer.MaxMessageSize := 16 * 1024 * 1024;
oServer.Active := True;
安全的帧长度解析
WebSocket 帧可以携带一个 64-bit 长度字段。根据 RFC 6455,该字段的最高有效位必须为零。sgcWebSockets 现在会拒绝其 64-bit 长度设置了高位的帧,而不是信任它,因此上述大小限制无法通过一个会回绕的整数被绕过。此项检查始终开启,且不依赖于 MaxMessageSize。
更严格的握手
SecurityOptions 下的两个新选项加固了 WebSocket 升级本身,且两者都默认启用。
EnforceWebSocketVersion 会对任何请求 13 以外版本的握手以 426 Upgrade Required 和一个 Sec-WebSocket-Version: 13 标头作出响应,而不是完成升级。ValidateWebSocketKey 会以 400 Bad Request 拒绝任何 Sec-WebSocket-Key 缺失或不是有效的 16 字节 base64 随机数的握手。两项检查仅适用于 RFC 6455 路径,因此基于旧规范的较老客户端不受影响。
oServer := TsgcWebSocketServer.Create(nil);
oServer.SecurityOptions.EnforceWebSocketVersion := True; // 426 on a wrong version
oServer.SecurityOptions.ValidateWebSocketKey := True; // 400 on a malformed key
// lock the server to your own site while you are at it
oServer.SecurityOptions.OriginsAllowed := 'https://app.example.com';
oServer.Active := True;
它如何与防火墙配合
这些保护机制位于协议层,在帧解析器内部,而这正是内存攻击发生的地方。这部分是 Firewall 和 RateLimiter 组件无法触及的,因为它们只在消息被解码之后才能看到消息。这两层相辅相成:继续使用防火墙和速率限制器进行 IP 过滤、连接和消息速率限制以及来源策略,而让新的内置限制守护解析器本身。对于公共服务器,我们建议将 MaxMessageSize 设置为你的真实最大值,将 OriginsAllowed 锁定到你的前端,并限制 MaxConnections。
升级
新的保护机制在你更新后即刻生效,且带有安全的默认值,因此大多数服务器无需更改一行代码即可获得内存和握手加固。如果你的应用程序确实合法地交换大于 64 MB 的消息,请相应地调高 MaxMessageSize。现有的客户端代码不受影响。
从 sgcWebSockets 下载页面更新,或通过 GetIt 或你的注册账户获取。
有问题、反馈或需要迁移帮助?联系我们,你将收到编写这些代码的人的回复。
