加固 sgcWebSockets 中的 WebSocket 服务器

· 组件

面向公共互联网的 WebSocket 服务器是一个诱人的攻击目标。该协议没有内置的消息大小限制,因此单个连接可以尝试用一个精心构造的帧拖垮整个进程,而升级握手则是探测松散验证的一个容易下手的地方。最新的 sgcWebSockets 提供了一组默认关闭这些大门的保护机制,使你的服务器即使在遭受恶意流量时也能保持运行。

以下所有内容适用于全部三种 WebSocket 服务器:TsgcWebSocketServerTsgcWebSocketHTTPServer 以及基于 http.sys 的 TsgcWebSocketServer_HTTPAPI。.NET 版本继承了相同的保护机制,因为它运行在相同的原生库之上。

一个限制阻止三种内存攻击

最重要的新增功能是一个单独的属性 MaxMessageSize,它限定了入站消息的最大大小。它默认为 64 MB,对几乎所有应用程序而言都很充裕,你可以调高或调低它,或将其设置为 0 以禁用该限制。

这一个属性可防御三种不同的内存耗尽技术,它们最终都以同样的方式收场,即服务器内存耗尽:

在每种情况下,连接都会以 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;

它如何与防火墙配合

这些保护机制位于协议层,在帧解析器内部,而这正是内存攻击发生的地方。这部分是 FirewallRateLimiter 组件无法触及的,因为它们只在消息被解码之后才能看到消息。这两层相辅相成:继续使用防火墙和速率限制器进行 IP 过滤、连接和消息速率限制以及来源策略,而让新的内置限制守护解析器本身。对于公共服务器,我们建议将 MaxMessageSize 设置为你的真实最大值,将 OriginsAllowed 锁定到你的前端,并限制 MaxConnections

升级

新的保护机制在你更新后即刻生效,且带有安全的默认值,因此大多数服务器无需更改一行代码即可获得内存和握手加固。如果你的应用程序确实合法地交换大于 64 MB 的消息,请相应地调高 MaxMessageSize。现有的客户端代码不受影响。

sgcWebSockets 下载页面更新,或通过 GetIt 或你的注册账户获取。

有问题、反馈或需要迁移帮助?联系我们,你将收到编写这些代码的人的回复。