我们正在使用 Improbable 的gRPC-Web 库将 gRPC 服务(在 Go 中实现)公开给在浏览器中运行的 Javascript 客户端。该服务将与现有的前端 Go 服务并列,该服务托管基于 REST 的 API。现有服务使用基于会话的身份验证来对其用户进行身份验证(会话 cookie + XSRF 保护,带有双重提交 cookie,也使用某些每个会话的服务器端状态进行验证)。
前端 Go 服务托管各种 API 端点,这些端点要么在本地处理,要么通过将请求代理到其他服务来实现。所有端点都通过 Gin 中间件处理程序链公开,该处理程序链实现上述会话身份验证和 XSRF 保护检查。有人建议我们在这个现有的中间件后面托管 gRPC-Web 的gogrpcproxy 组件,以向世界公开我们的 gRPC 服务。
我有兴趣确保验证传入 gRPC-Web 请求的方法是安全的。提出了以下方法:
基于令牌的身份验证——即在 gRPC 请求元数据中传递承载令牌,由后端 gRPC 服务验证。如果不涉及 gRPC-Web,这与验证原生 gRPC 调用的验证模型相匹配。
在这个模型中,gRPC-Web 的职责是实现浏览器和服务器之间的传输抽象,以及编组到/来自本机 gRPC 表示的请求;身份验证被委托给支持 gRPC 服务。gRPC-Web 代理作为现有 REST API 外部的单独端点托管。
基于会话的身份验证——重用现有的会话身份验证中间件。在此模型中,grpcweb 代理服务器托管在 Gin 处理程序链后面。在接受请求之前,Gin 会执行常规检查以验证相关 cookie 和 XSRF 标头是否存在。
这种方法重用了大部分现有的身份验证逻辑。但是,它需要传递 XSRF 标头以确保请求被 Gin 中间件接受。这在当前实现中可以通过设置请求元数据来实现,该元数据(当前)通过在出站 HTTP 请求上设置标头来实现。但是,我不清楚这是否:
- 是合适的,因为通过利用元数据当前作为 HTTP 标头传递的实现细节,这似乎是违反层的。这没有记录在案,并且可能会发生变化;
- 与 gRPC-Web 的 websocket 传输兼容,它似乎不会将元数据传播到标头中,因为 websocket 传输在传输任何请求之前就已拨号;
- 将来会遇到潜在的安全问题,因为与前端服务的长期 gRPC-Web 传输连接仅在首次建立时由前端代理进行身份验证,而不是在每个请求上持续进行身份验证(除非 gRPC 服务也验证请求元数据)。
我的理解是 gRPC-Web 试图模拟浏览器和服务器之间的 gRPC 传输,因此没有实现特定的身份验证逻辑。用于传递身份验证详细信息的标准 gRPC 机制不允许隐式基于会话的状态,因此我更喜欢基于令牌的方法。
这是对可用选项的合理分析吗?