[bug] grpc-gateway POST请求触发随机路由问题
背景
service层部分组件使用了grpc-gateway,用来在支持grpc协议的同时,额外导出一个http端口,并实现自动转换。 访问http端口的请求,会被grpc-gateway解析并匹配到与proto中配置一致的路由上。
问题
grpc-gateway支持在POST表单时,允许请求“fallback”到相同路由的其他method上,这在初始化时是默认开启的。
简言之,当http请求为POST且带有Content-Type: application/x-www-form-urlencoded的header时,如果server没有注册POST-handler,则会默认fallback到相同路由下的其他method处理。同时,由于grpc-gateway的路由mux实现是保存在map中的,遍历到第一个同路由method时就会执行fallback,结合golang中map遍历的随机性,这会导致以下场景下的随机路由问题:
- server定义了某一条资源路由规则下的get/put/delete handler,但没有post
- 用户错误地发起一个post请求到该资源,并且带了
Content-Type: application/x-www-form-urlencoded(这在curl -d的情况下是默认的行为)。 - 预期中,由于该资源路由下我们没有定义post handler,应该返回405 METHOD NOT ALLOWED,但事实上,grpc-gateway会随机地把请求打到该资源路由下的get/put/delete handler上。
虽然当前bcs几乎没有表单POST的场景,但这是一个极大的安全隐患,带来的随机性和不确定性对服务数据有很大的威胁(一个错误的提交可能会删掉所有的数据)。
影响范围
当前直到v2.7.3的grpc-gateway版本都有这个默认开启的问题,bcs-service用到grpc-gateway的组件都存在这个问题。
解决办法
grpc-gateway默认开启的PathLengthFallback功能决定了这个行为,我们并不需要这个功能,因此我们在初始化grpc-gateway的时候,使用WithDisablePathLengthFallback选项手动将其关闭,即可规避这个问题。
rmMux := ggRuntime.NewServeMux(
...
ggRuntime.WithDisablePathLengthFallback(),
)
各个模块可以参考bcs-helm-manager的commit来处理。