强化 sgcWebSockets 中的 HTTP 服务器

· 组件

WebSocket 服务器几乎总是同时也讲 HTTP:它提供打开套接字的页面、响应健康检查、托管 API,或重定向到你的前端。这个 HTTP 面有其自身一组众所周知的攻击类别,而最新的 sgcWebSockets 默认关闭了其中最重要的几类。这适用于两种 HTTP 服务器:基于 Indy 的 TsgcWebSocketHTTPServer 和基于 Windows http.sys 的 TsgcWebSocketServer_HTTPAPI

封堵路径遍历

如果你通过设置 DocumentRoot 来提供静态文件,服务器过去是通过将文档根目录与请求路径拼接来构建文件路径的。像 GET /../../../../windows/win.ini 这样的请求,或其 URL 编码形式 /..%2f..%2f...,可能会越出 web 根目录并读取任意文件。现在服务器会对解析出的路径进行规范化处理,仅当其仍位于 DocumentRoot 内部时才提供服务;任何越界的内容都会被拒绝。此功能始终启用,在 HTTP/1.x 和 HTTP/2 两条路径上均有效,且无需任何配置。

限制请求体大小

HTTP 请求体没有内置的大小限制,因此客户端可以声明一个巨大的 Content-Length 并流式传输数 GB 数据,从而耗尽服务器内存。新的 MaxRequestBodySize 属性对其加以限制。它默认为 64 MB,声明超过该值的请求会在请求体被缓冲之前以 413 被拒绝。对于大型上传可调高它,或将其设置为 0 以禁用该限制。

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.Port := 80;
oServer.MaxRequestBodySize := 16 * 1024 * 1024;  // 16 MB, reject larger with 413
oServer.Active := True;

更严格的请求解析阻止走私

HTTP 请求走私利用的是那些对一个请求在哪里结束、下一个请求从哪里开始存在分歧的服务器,通常做法是同时发送 Content-LengthTransfer-Encoding: chunked 头。新的 StrictRequestParsing 属性默认启用,会以 400 拒绝此类含糊的请求,并应用更严格的 chunked 编码校验,使得前端代理与服务器无法被去同步化。

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.StrictRequestParsing := True;   // default: reject Content-Length + Transfer-Encoding
oServer.MaxRequestBodySize := 64 * 1024 * 1024;
oServer.Active := True;

HTTP/2 Rapid Reset 防御

HTTP/2 Rapid Reset 攻击(CVE-2023-44487)会打开一个流并立即用 RST_STREAM 将其取消,如此紧密循环,迫使服务器以几乎零代价于客户端的情况下执行无限的请求处理。现在 HTTP/2 服务器会限制单个连接可发送的重置和控制帧的数量,提供 100 个并发流的有限默认值,并通过 GOAWAY 帧关闭滥用的连接。HTTP/2 是需要主动启用的,因此只有在你启用了它时才相关,但如果你启用了,该防御是自动的。

更干净的响应头

当应用程序从受请求影响的数据(例如重定向目标或实体标签)构建响应头时,混入该值中的回车符或换行符可能会拆分响应并注入额外的头。现在 http.sys 服务器会从诸如 LocationServerETag 等响应头的值中剥除 CRLF,使得单个值再也无法变成两个头。

防火墙的定位

这些保护位于服务器自身的 HTTP 解析器和文件服务器中,而这正是 FirewallRateLimiter 组件无法触及的层。继续使用防火墙来进行 IP 过滤、速率限制和来源策略,并让内置的防御来守护解析与静态服务。对于公开的服务器,我们还建议仅在你确实需要提供文件时才设置 DocumentRoot,将 MaxRequestBodySize 调整为你真实的最大值,并对 MaxConnections 设上限。

升级

这些新保护在你更新后即刻生效,并带有安全的默认设置,因此大多数服务器无需更改代码即可获得这种强化。如果你的应用程序接受大于 64 MB 的请求体,请相应地调高 MaxRequestBodySize。现有客户端不受影响。

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

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