whatwg / fetch

Fetch Standard
https://fetch.spec.whatwg.org/
Other
2.1k stars 325 forks source link

Tighten `application/x-www-form-urlencoded` CORS safe-list carveout? #1706

Closed domfarolino closed 3 weeks ago

domfarolino commented 11 months ago

Right now cross-origin POST requests with the Content-Type: application/x-www-form-urlencoded header are safe-listed, and thus don't trigger CORS preflight requests. I believe this is to grandfather-in the fact that you could always make these requests with <form method=post> before preflight requests were a thing. However, my understanding is that we generally want these kinds of non-form-initiated requests to require preflights, hence the fact that we require preflights for other content types.

But other specs can simply use the Content-Type: application/x-www-form-urlencoded request header to avoid preflights on a request that really should send preflights. In fact, my understanding is that FedCM did this on accident (the spec was not trying to bypass the preflight requirement, they just thought it was the most sensible value). However I think it is Fetch's intention to require preflights for these kinds of requests (since they're "new", and not HTML form-initiated), so I'm wondering if we should change Fetch (and its implementations) to actually require preflight requests for POSTs even if they have the Content-Type: application/x-www-form-urlencoded request header, but if they are not specifically triggered by actual HTML forms.I'm now sure how Fetch would check this — I assumed we could check "initiator" but there doesn't seem to be a value for forms specifically.

I think that would prevent consumers from using this content-type on new, non-form-initiated requests while accidentally bypassing preflight requests without realizing it. Rather, these requests would get a preflight, because they aren't initiated from HTML forms and therefore shouldn't be grandfathered-in in the same way that HTML forms are.

Thoughts?

annevk commented 11 months ago

I think that would break service workers. Unless you add more shenanigans.

See also #838 for another tightening idea.

(You might also be interested in https://github.com/annevk/orb/issues/18.)

domfarolino commented 11 months ago

Yeah that's a good point. I guess the other shenanigans would be keeping the current carve-out behavior for service-worker-forwarded requests only if they came from a <form>? And I don't think we have all that information in a single request object, so that's probably a deal-breaker.

annevk commented 11 months ago

We do, both request's mode and destination give it away. But this exception has been there since the dawn of cross-origin XMLHttpRequest and I strongly suspect it's used. Not sure why I didn't gave that rationale in the earlier comment, maybe it escaped me. (It was also very much intentional to allow that without preflight contrary to what OP suggests.)

(By the way, the grandfather clause is probably best avoided given its origin: https://en.wikipedia.org/wiki/Grandfather_clause.)

domfarolino commented 1 month ago

(It was also very much intentional to allow that without preflight contrary to what OP suggests.)

Just so I'm clear, you're saying it was always intentional that cross-origin application/x-www-form-urlencoded non-<form>-initiated requests not require a preflight? I don't know the history around all of this, but that surprises me a little bit. If the intention was to always have all kinds of "form urlencoded" requests bypass preflights, then you can basically treat application/x-www-form-url-encoded as application/please-bypass-preflight anywhere you can specify it.

annevk commented 3 weeks ago

Correct. The history is that we added preflights for cross-origin requests you could not already make. I.e., everything you could already do with <form> was not intended to be protected. Inadvertently we did widen the number of requests you could make without preflight a little bit due to the flexibility of XMLHttpRequest versus <form> and as a result some of that has been tightened over the years to the extent deemed necessary by the security teams involved.

To add, I think it is okay when other specifications use the application/x-www-form-url-encoded value and follow the rules of the format. If they start sending JSON with that type it would be wise to rein it in.