Value "wss" for HTTP Header "X-Forwarded-Proto" should be consistently handled #27084
Labels
for: external-project
For an external project and not something we can fix
status: invalid
An issue that we don't feel is valid
I have noticed that a HTTP(S) request with the header
X-Forwarded-Proto: wss
can cause problems in the Spring Boot web stack, at least with Tomcat + Spring MVC (+ Spring Websocket).X-Forwarded-Proto
is not standardized, but nevertheless a heavily used header. In the past (or currently still) there seems to be an assumption that only "http" and "https" are valid values (see e.g. MDN and other sources when searching for the header name on the web).But at least one popular proxy, Traefik, has decided to use "ws" and "wss" in the header, see traefik/traefik#6388. When using Traefik as a reverse proxy in front of Spring Boot, we ran into issues with Websockets because of incorrect or inconsistent handling regarding the said header in the web/servlet stack.
It would be good if Spring Boot could ensure via configuration, code, and/or working with upstream dependencies, that "wss" is handled consistently like "https" in all situations. Based on my observations I would propose that
X-Forwarded-Proto: wss
gets mapped into theServletRequest.scheme
value "https" (and "ws" -> "http") because that seems the most compatible way.Putting "wss" into
ServletRequest.scheme
seems to lead to problems. A same-origin check that compares the value of theOrigin
header (where "https" would be the protocol) with the servlet request url would fail, as did happen for us in the Spring Websocket code. I also know of at least one library that fails completely when it encountersscheme=wss
in theServletRequest
object.The following examples show the instances of incorrect/inconsistent handling of the header that we found during debugging our problem. There might be other places.
Tomcat RemoteIpValve
If
server.forward-headers-strategy
is set to "native", then Spring Boot adds theRemoteIpValve
to Tomcat. This valve currently maps a "wss" header toscheme=http, secure=false
. This is clearly incorrect.Note that Tomcat has added handling for "ws" and "wss" into their websocket code (i.e. they have recognized the issue as well): apache/tomcat#311. But there remain hardcoded checks in other places as shown above. I've not opened an issue with Tomcat, since I thought to leave it for you to decide how and where to handle this issue.
Spring Web
If
server.forward-headers-strategy
is set to "framework", then Spring Boot registers aForwardedHeaderFilter
. This filter and its dependencyUriComponentsBuilder.fromHttpRequest(request)
have hardcoded checks for "http" and "https" in multiple places ([1], [2]), but leaves an unrecognizedX-Forwarded-Proto
value alone. Depending on which otherX-Forwarded
headers are present, it maps a https/wss request on port 443 to the following servlet properties:scheme=wss, port=80, secure=false
orscheme=wss, port=443, secure=false
. This is incorrect, and as mentioned before,scheme=wss
does not seem very compatible anyway.The text was updated successfully, but these errors were encountered: