Open vv2020 opened 5 days ago
@vv2020, can you please detail whether all other same-origin requests to the server work, and it is only /logout
that does not work?
What HTTP method is used for /logout
? Just GET
or POST
?
If the CrossOriginHandler
handles the request, it is because it has an Origin
header, so likely is it not a GET
request.
Unfortunately there is no specification for the server-side, so the server would have to match the Origin
header with scheme+authority, and this may be complicated due to:
Host
or :authority
header)Allowing same-origin requests makes sense, it is the implementation that is delicate, and I'm worried to open up a security hole.
For example, Forwarded
is not a forbidden header.
A script from evil.com
can craft a request:
POST / HTTP/1.1
Host: good.com
Origin: evil.com
Forwarded: host=evil.com
The server could choose to compare Origin
with Host
, but would break legit proxies.
Or compare Origin
with Forwarded[host]
and be exploited.
Makes sense?
@gregw thoughts?
@sbordet Yes, It is a post request and it blocks all the request not just those to /logout.
I would also be interested to know, what Jetty CORS would consider as same-origin ?
@vv2020 it was always the case, even with the now deprecated CrossOriginFilter
(replaced by CrossOriginHandler
), that you had to be explicit also for same-origin in the configuration.
There was never a concept of "same-origin".
Comparing Origin with Host looks reasonable, though I’m not entirely clear on how this would break legitimate proxies.
The proxy receives a request with Host: good.com
from the client, but then it forwards it towards one of the backends, so the request to the backend has Host: backend1:8080
, which obviously would fail the comparison.
But then the proxy would send Forwarded: host=good.com
in the request to backend1
.
@sbordet I hear you, but I don't see it will break legit proxies, From what I understand, the proposed behavior introduces an additional check for same-origin requests, but doesn't change the existing behavior regarding how the Origin header is validated.
For Example Consider below scenario :
Client initiates a request with Host : good.com
and with Origin : good.com
now it intercepts by a proxy and changes to Host : by-proxy.com
and origin as Origin : good.com
.
Current Behaviour :
Jetty-cors checks whether it has Origin.
Proposed Behaviour :
Jetty-cors checks whether it has Origin.
So, based on this, it seems that we are not altering the existing behavior but simply enhancing it by allowing same-origin requests to pass through without blocking.
@vv2020 in your proposed solution, where would Jetty retrieve "by-proxy.com" to compare with the value of the Origin
header?
If Host
header, HTTP/2 and HTTP/3 requests typically do not have the Host
header at all (it could be synthesized with HostHeaderCustomizer
though).
From your point of view of the application developer, you would need to configure differently your application, or Jetty, knowing in advance whether it will be deployed behind a proxy (in which case you need to configure CrossOriginHandler.allowedOriginPatterns=good.com
), or not (in which case no configuration is necessary).
At this point, you just add CrossOriginHandler.allowedOriginPatterns=good.com
and it will work in every case.
We will consider your proposal though: for simple deployment setups it may be a good default.
Typically, jetty will only inspect Forwarded
headers if it has the ForwardedRequestCustomer
enabled, which is typically only done when Jetty is behind a proxy. So either:
a) Jetty is not behind a proxy, and the request is not customized, so the Forwarded headers are ignored and the request's authority will be good.com
rather than bad.com. Thus the authority can be trusted; or
b) Jetty us behind a proxy, and the request is customized, the Forwarded headers are inspected and the request's authority will be evil.com
. HOWEVER, in this case, it is the responsibility of the proxy to not let through any bad/fake Forwarded headers. If the proxy is good AND the ForwardedRequestCustomizer
is well configured, then the authority can be trusted.
Note that it is difficult to determine if jetty is behind a proxy, as the server can have multiple connectors, with different customizers. Thus on every request we'd have to get the connection, get the connector, find the HttpConnectionFactory, get the HttpConfiguration, get the customizers, look for the ForwardedRequestCustomizer
. That's a bit of work and still does not tell us if the ForwardedRequestCustomizer
is well configured or not (to accept exactly only the headers the proxy actually manages).
So it would be difficult to check the configuration to automatically determine if the authority can be trusted or not.
However, I do see the flip side, that it can be a pain to have to configure an explicit hostname in the CORS configuration.
Perhaps as a compromise, we add a configuration to the CORS handler that says setTrustAuthority(boolean)
, which is default set to false. But a user, who takes on the responsibility of carefully configuring the proxy setup can just set that to true, so they now trust the authority of the request, no matter where it comes from. This still requires a configuration step, but rather than hard coding a hostname, it is just a declaration that they have done the the work required to trust the hostname in the authority.
Jetty version(s) jetty 12.0.12
Jetty Environment ee10
Java version/vendor
(use: java -version)
java 17OS type/version Ubuntu 20.04
Description We are migrating CORS validation from spring's cors filter to jetty cors (specifically using the jetty-cross-origin.xml file), after completing the migration, we encountered an issue with the /logout endpoint being unreachable.
While troubleshoot we initiate a post request from js to "/logout" endpoint and found jetty is blocking this request and replying with 400 " Origin not allowed".To clarify, our environment consist both js pages and application on same jetty server, so scheme://domain:port is same for all the request initiated from js to server, yet jetty blocks the request.
Further investigation revealed that Jetty appears to be blocking what seems like a same-origin request. For Example : when the Origin header is set to "http://localhost:8081" and the request is made to our application URL at "http://localhost:8081/sample/logout", Jetty is blocking the request.
We were wondering if this a expected behavior or a bug, If this is a expected behavior, we would appreciate it if you could provide documentation or any resources that explicitly mention this scenario.
How to reproduce? Initiate a request with Origin with the same url of application server.