Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do we want to allow web developers to add headers of the CONNECT request? #263

Open
yutakahirano opened this issue May 17, 2021 · 54 comments
Labels
Discuss at next meeting Flags an issue to be discussed at the next WG working

Comments

@yutakahirano
Copy link
Contributor

yutakahirano commented May 17, 2021

Web developers may want to attach some information to the request, and encoding everything into the URL is tiresome.

The simplest form is something like this.

const wt = new WebTransport(url, {headers: {foo: 'bar'}});

This may be dangerous if web developers can specify arbitrary headers. Some examples:

new WebTransport(url, {headers: {origin: 'https://google.com/'}});
new WebTransport(url, {headers: {host: 'mozilla.org'}});

So, we need to forbid forbidden header names at least.

That may not be enough. For usual HTTP requests, if the request is cross-origin and has non-safelisted headers, CORS preflight is needed. WebSocket solves the problem by allowing web developers to control only one header (sec-websocket-protocols). We can do the same for WebTransport.

Thoughts?

@yutakahirano
Copy link
Contributor Author

@annevk @mikewest in case they are interested.

@annevk
Copy link
Member

annevk commented May 17, 2021

I'm not sure I understand the setup. I thought WebTransport would use ALPN. It seems to me that once ALPN is okay with a specific WebTransport identifier, everything after that is okay as it would be part of the protocol?

@yutakahirano
Copy link
Contributor Author

yutakahirano commented May 17, 2021

I'm not sure I understand the setup. I thought WebTransport would use ALPN. It seems to me that once ALPN is okay with a specific WebTransport identifier, everything after that is okay as it would be part of the protocol?

True, the client must not start establishing a WebTransport session before receiving SETTINGS_ENABLE_WEBTRANSPORT. With that settings, is it OK to send arbitrary headers (as long as the request makes sense in the context of WebTransport over HTTP/3)?

@annevk
Copy link
Member

annevk commented May 17, 2021

I think so. WebSocket and fetch() have to care about existing servers and infrastructure and therefore come with a bunch of restrictions. WebTransport only has to care about such restrictions (e.g., ports, CSP) up until the point a connection has been established as after that it should be clear to the server it's something new.

We should make this clear in the WebTransport specification (e.g., in a considerations for server implementers section) and hopefully devrel will also cover this extensively.

@ricea
Copy link
Contributor

ricea commented May 18, 2021

I think from a privacy and security perspective this looks okay. We probably want to block request-body-header names too since although they wouldn't create security problems they'd be very confusing if set.

I assume extensions will be able to read and modify the headers?

I'm not sure what I feel about this from a design perspective. Is this something we're going to regret later?

@vasilvv
Copy link
Contributor

vasilvv commented May 18, 2021

My personal intuition on this:

  • Is having headers useful for developers? I'd say yes; e.g. if I am writing an application for streaming video, I could negotiate video format (I have a design of this nature that I want to do on top of WebTransport). I suspect @afrind or @kixelated might have opinions.
  • What headers should we allow? I think we should allow headers by default. We definitely want to reserve Sec-* and Origin for the user agent, as we rely on Origin header being supplied by ourselves. https://fetch.spec.whatwg.org/#forbidden-header-name is probably the easiest approach here.
  • Should we do something like CORS? Since we are starting from scratch here, WebTransport servers know that they may receive cross-origin requests, so I feel like the answer here is no. We may add some form of confirmation protocol (e.g. make WebTransport server echo the origin), but I am not sure there is much value in this.

@annevk
Copy link
Member

annevk commented May 18, 2021

I guess this is about about session establishment as written down in https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3#section-3.3? So in theory that is post-ALPN so the server should know what is going on. However, I guess there is the concern servers might agree to the ALPN handshake but then handle the request in a normal endpoint (for authentication purposes and such), similar to what has been happening with WebSocket.

I suspect we would never include cookies or HTTP authentication in this CONNECT request, unlike WebSocket?

@wilaw wilaw added the Discuss at next meeting Flags an issue to be discussed at the next WG working label May 18, 2021
@vasilvv
Copy link
Contributor

vasilvv commented May 19, 2021

I'm not sure what ALPN specifically has to do here, since they both have ALPN of h3.

The CONNECT requests are supposed to be fully uncredentialed, that is to say, no cookies, no HTTP auth, no TLS client certificates.

@annevk
Copy link
Member

annevk commented May 20, 2021

Sorry, I meant that the TLS handshake indicates it's a WebTransport connection and not a "normal" HTTP connection. I see that's covered through SETTINGS_ENABLE_WEBTRANSPORT as @yutakahirano already mentioned above.

@vasilvv
Copy link
Contributor

vasilvv commented May 20, 2021

Just to clarify, WebTransport support is an additive property; normal HTTP traffic can occur on WebTransport-supporting connections, though by default a WebTransport object will create a new connection.

@annevk
Copy link
Member

annevk commented May 20, 2021

Thanks! That does argue for being conservative with the handshake as servers will be incentivized to flip the WebTransport setting and are not at all incentivized to audit all their "legacy" paths at the same time.

@yutakahirano
Copy link
Contributor Author

Given the discussion above, do the followings make sense?

  • Web developers can append request headers as part of the constructor parameter.
  • If headers contains https://fetch.spec.whatwg.org/#forbidden-header-name or headers the user agent would attach (e.g., datagram-flow-id), then the constructor throws.
    • Alternative: They are ignored.
    • Alternative: The session will fail but the constructor doesn't throw.

@annevk
Copy link
Member

annevk commented May 24, 2021

I think to be on the safe side you probably want to match WebSocket or "no-cors". Other headers would only come after a CORS preflight normally, after all.

@yutakahirano
Copy link
Contributor Author

Ah sorry I overlooked the last comment.

@jan-ivar
Copy link
Member

jan-ivar commented May 25, 2021

Meeting:

  • Reluctance to allow arbitrary headers (maybe 1 header?)
  • Might it suffice to negotiate in the URL? (url is 8k for memory)
  • Sec-Webtransport-Extra-Stuff?
  • Martin: Let's not use prefixes or Sec
  • We don't need CORS pref-light (bad for performance) since we have settings
  • Use URL or put it in application data for now. Defer until new interest emerges

@kixelated
Copy link

Oh sorry I thought I hit the comment button.

My application needs to negotiate a video codec on connect. The client sends a list of supported codecs and the server responds with the chosen one. TLS does something similar.

There's how you could implement this:

  1. Implement a custom RPC using streams and buffer streams until both sides receive some sort of INIT message.
  2. Send supported codecs in a custom request header and the chosen codec in a custom response header.

You could marshal the supported codecs into the request path, but it's a pain. The server still needs to respond with the chosen codec and that's not possible.

I would definitely use custom request+response headers if they were available. Only supporting custom request headers would be a marginal improvement.

@annevk
Copy link
Member

annevk commented May 26, 2021

We don't need CORS pref-light (bad for performance) since we have settings

What settings is this referring to? If it's the settings set during ALPN I think that is negated (as also stated in prior comments) by the fact that this handshake ends up intermixed with normal HTTP traffic.

@yutakahirano
Copy link
Contributor Author

@kixelated

Thank you for your opinion!

@annevk
What settings is this referring to? If it's the settings set during ALPN I think that is negated (as also stated in prior comments) by the fact that this handshake ends up intermixed with normal HTTP traffic.

I believe it's a new setting - we can define a new setting listing headers which the server accepts for the CONNECT request for webtransport over HTTP/3, for example (let's call it SETTINGS_WEBTRANSPORT_REQUEST_HEADERS for now). Just like SETTINGS_ENABLE_WEBTRANSPORT, if the server supports WebTransport over HTTP/3 and accepts custom headers for the CONNECT request, it should include SETTINGS_WEBTRANSPORT_REQUEST_HEADERS in the initial SETTINGS frame.

This is less flexible than CORS preflight but more efficient.

@annevk
Copy link
Member

annevk commented May 26, 2021

I see, that would be acceptable (to me, at least). I wonder though, could we generalize that to SETTINGS_BYPASS_CORS_PREFLIGHT or some such so we solve that thorny problem at the same time? See quicwg/base-drafts#1993. Folks (I recall @mnot?) did raise similar concerns in that the person configuring TLS might not be aware of all the HTTP services on the server, but I think we'll always have that problem for any kind of centralized solution. I would hope that documentation would go a long way towards addressing that.

(I could also see trying this more limited thing out first and if it works going for the generalized solution later. Dunno how cheap these bits are.)

@mnot
Copy link
Member

mnot commented May 26, 2021

Flipping this bit in a SETTING (h2 or h3) has significant performance / race avoidance advantages, so I'm interested. Yes, there are deployments where server administration isn't done by the same people, but I think we can address that risk by:

  1. Requiring implementations to ship with this off (and explaining why that is)
  2. Recommending documentation to put next to the control for the setting, so that users have that information at hand

It might also be interesting to define a companion well-known resource (or Origin Policy property, if that gets off the ground), which would allow control over the SETTING through a resource that's available to the server's content folks (provided that server implementations link the two).

I'm happy to write up a draft for the SETTING, but first it'd be great to hear what people like @mikewest think about this.

@yutakahirano
Copy link
Contributor Author

cc: @martinthomson

@martinthomson
Copy link
Member

I'm not seeing any significant new information: yes, this sort of capability might be nice, but it also costs a fair bit to add. It can be added later.

@jan-ivar
Copy link
Member

Meeting:

  • Seems like something we can add later, modulo perhaps some issues with WebExtensions

@yutakahirano yutakahirano removed the Discuss at next meeting Flags an issue to be discussed at the next WG working label Jun 23, 2021
@yutakahirano yutakahirano added this to the Uncategorized Future milestone Jun 30, 2021
@jlpettersson
Copy link

It would be useful to pass a JWT token for authentication / authorization in the connect request. On the server-side, TLS connections are typically terminated by a load balancer, but it would be useful if an JWT token could be passed through to the server instance behind the load balancer.

the fetch api has "authentication entries" for this.

@annevk
Copy link
Member

annevk commented Feb 10, 2022

Authentication entries is explicitly not web developer controlled though.

@randomstuff
Copy link

See the related bug entry about the lack of support for HTTP authentication in the WebSocket API.

@matthewp
Copy link

matthewp commented Jun 17, 2024

How do we implement authentication with Web Transport? Do we need to store the session token in a non-HTTPOnly cookie or localStorage or something and send it (from the client) as the first message of a session?

@MarcoPolo
Copy link

I'd like to link this issue from the WebTransport overview IETF draft: ietf-wg-webtrans/draft-ietf-webtrans-overview#10.

At the last IETF meeting (120), the linked issue was brought up (minutes). There was no objection to removing the current prohibition on HTTP Authentication, which was probably due to an earlier version of WebTransport that was less tied to HTTP.

To me, (and others judging from WebSocket history at whatwg/websockets#16) it seems preferred to build on top of standard HTTP Authentication schemes rather than relying on less well-defined custom workarounds (like using the URL). While we can defer this, folks are going to need authentication, and the longer we defer the more ad-hoc solutions will pop up. It'll be a worthwhile investment to get this right sooner rather than later. In case it's easier, I think even just supporting authentication related headers for now would be better than nothing.

@ricea
Copy link
Contributor

ricea commented Sep 23, 2024

The reason for disallowing HTTP authentication is that it ties WebTransport to the HTTP stack, making it harder to implement and opening the floodgates to other HTTP features such as cookies. Experience with the problems this caused for WebSocket implementations was a major driver for this decision.

Permitting authentication and other headers to be set from JavaScript seems less problematic, as long as we are confident that servers won't be tricked into bypassing the same-origin policy.

@mnot
Copy link
Member

mnot commented Sep 24, 2024

The reason for disallowing HTTP authentication is that it ties WebTransport to the HTTP stack, making it harder to implement and opening the floodgates to other HTTP features such as cookies. Experience with the problems this caused for WebSocket implementations was a major driver for this decision.

Without commenting on whether or not HTTP auth is appropriate for WebTransport -- these aren't very convincing reasons.

There have been many attempts at creating "protocol neutral" stacks eg SOAP / WS-* -- the reality is that aspects of the underlying layers do sometimes peek through abstractions, necessarily. HTTP Authentication is already widely implemented so supporting it doesn't raise costs significantly. And the ability to choose what features might be useful is hardly "opening the floodgates."

@vasilvv
Copy link
Contributor

vasilvv commented Sep 24, 2024

Even ignoring the question of costs, I think HTTP auth in browsers is generally a terrible feature and we should not bring it to the new APIs. It has dubious privacy properties (is there even an API for logging out?), even worse UX properties (locking up webpage loading with browser-level models for a random subresource), and WebTransport brings its own concerns (serverCertificateHashes).

@annevk
Copy link
Member

annevk commented Sep 24, 2024

We generally don't show dialogs for subresource fetches anymore though. If the user was not previously authenticated for that resource the fetch simply fails. It seems reasonable to prevent it altogether here, though I'm a bit less persuaded that not doing the normal thing for cookies is also good. I suspect that will make it harder for routing middleware to do the right thing.

@FZambia
Copy link

FZambia commented Oct 26, 2024

Worked with WebSocket protocol quite a lot. The lack of possibility to set custom header in browser results into various hacks:

  • sending tokens as part of URL
  • sending auth info in subprotocols
  • sending auth info in first message (while it seems less hacky, it still comes with extra RTT)

People always come with questions how to set headers for WebSocket in browsers to reuse existing infrastructures. While in case of Fetch API (including streaming case) all the initial info may be sent in headers or request body – which is effective, obvious and flexible.

I've read the discussion, but still do not clearly see why such a functionality can't be added - if it works for Fetch, why it can't be done for WebTransport in browser environment? It seems WebTransport should mainly target browsers and browser specifics even if it can be re-used in different environments? Since it's mostly for browsers, delaying implementation for v2 of WebTransport object can result into a situation we've been many times - some browsers support it, some does not, and all the bunch of workarounds again.

My main concern here TBH is not adding the possibility to set custom headers specifically, but possibility to pass some additional custom payload without a need for extra RTT (like we can using Fetch API). Probably it can be done in a different way with WebTransport.

Another question I have now after reading the discussion – will WebTransport in the browser include Cookie for requests on the same domain to at least match with current WebSocket behaviour? It would be nice to match in the behaviour here for easier adoption.

@wilaw wilaw added the Discuss at next meeting Flags an issue to be discussed at the next WG working label Oct 30, 2024
@jan-ivar
Copy link
Member

Meeting:

  • question 1: do webtransport requests have cookies? No. A mistake in WebSockets?
  • In theory, flexibility in headers seems nice, metadata fields, extended connect schemes
  • In reality, there are security considerations. In the past we've said let's not allow websites to add that
  • Once we allow passing authentication credentials it alters the security concerns
  • For workarounds, the server would need to accept the connection first, better to not accept the connection
  • MOQ does it per subscribe
  • potential proposal: allow to manually set HTTP authorization header? use case?
  • too limiting? Why not send arbitrary headers? In fetch we kept adding headers to a block list. Maybe that's fine?
  • @martinthomson ^ thoughts?
  • Use cases:
    • you want to use HTTP headers for authentication??, an abitrary subset of HTTP
    • Currently not providing a good interaction with middleware proxies load balancers
  • The WG is looking for more input whether this is a good idea or not.
  • MOQ carries cache information without relying on the HTTP concept (RFC 110). You can always put headers into the payload
  • Keep discussing

@annevk
Copy link
Member

annevk commented Nov 19, 2024

It strikes me as a mistake to not include cookies or user-agent supplied HTTP authentication data. I don't see how it changes the security concerns as this is already the default for all HTTP requests. What WebTransport is doing deviates from that and will likely make deployment harder due to inflexible middleware.

Also, you cannot allow cross-origin headers to be set without CORS in an HTTP handshake, that especially includes the Authorization header.

@jan-ivar
Copy link
Member

jan-ivar commented Dec 4, 2024

Meeting:

@jan-ivar
Copy link
Member

Meeting:

  • gecko leans toward negative but not strongly
  • continue discussion

@wilaw
Copy link
Contributor

wilaw commented Dec 18, 2024

@annevk - re

It strikes me as a mistake to not include cookies or user-agent supplied HTTP authentication data.

What API surface might you have in mind? What restrictions - i.e leveraging forbidden header names. ?

@annevk
Copy link
Member

annevk commented Dec 18, 2024

I don't understand the question.

@wilaw
Copy link
Contributor

wilaw commented Dec 19, 2024

My apologies, let me rephrase it more explicitly - you had indicated that it would be a mistake to not include cookies or UA supplied HTTP auth data. Therefore, it should be included in the initial connection. Since we have no means of doing this with the current API surface, we need to extend the API to accomplish this inclusion. One proposal has already been presented.

const transport = new WebTransport(url, {
        headers: {
            'Authorization': `Bearer ${token}`,
        },
    });

To that you had previously commented

Yeah, that's the same kind of use case people have for including headers in the WebSocket handshake. As discussed many months ago upthread the conservative option would be requiring a CORS preflight for that, along with the necessary infrastructure. That doesn't seem like the kind of thing that should go into v1.

So if we want to include cookies and auth data in v1, is there a manner of accomplishing it which WebKit would find acceptable? That API + CORS + FETCH FORBIDDEN HEADERS, or a different API, or some other combination of constraints?

@annevk
Copy link
Member

annevk commented Dec 19, 2024

No you don't need to change the API for what I'm saying. You just don't make the fetch with credentials mode set to "omit". Instead you'd use "include", just like WebSockets: https://websockets.spec.whatwg.org/#websocket-opening-handshake.

Letting the web developer set credentials would indeed require API surface and would require CORS and such. That's much more involved and is completely separate.

@vasilvv
Copy link
Contributor

vasilvv commented Dec 19, 2024

I think the question of whether WebTransport requests should be credentialed is distinct from the one that's asked in this issue.

For what it's worth, unless we make them credentialed, I don't see any circumstances under which a WebTransport request would require a CORS preflight, regardless of whether it has application-specified headers or not.

@annevk
Copy link
Member

annevk commented Dec 19, 2024

It is, it was just that it was brought to my attention here as part of the meeting summary above. I filed #625 on the broader issue as there might well be more than just "credentials mode" I suspect.

@annevk
Copy link
Member

annevk commented Dec 19, 2024

I don't see any circumstances under which a WebTransport request would require a CORS preflight, regardless of whether it has application-specified headers or not.

Well, the same-origin policy also guards IP-address-authenticated resources. So please don't do that.

@vasilvv
Copy link
Contributor

vasilvv commented Dec 20, 2024

Well, the same-origin policy also guards IP-address-authenticated resources. So please don't do that.

Don't do what? WebTransport already does not do CORS preflights (neither does WebSocket, for that matter).

@repulsio
Copy link

Can we please have this?

@wilaw
Copy link
Contributor

wilaw commented Jan 17, 2025

Linked to #264

@jan-ivar
Copy link
Member

jan-ivar commented Jan 24, 2025

I think from a privacy and security perspective this looks okay.

What headers should we allow? I think we should allow headers by default.

Can we please have this?

This thread has gotten long. We might need to clarify what "this" is. Are anyone's requirements not met by something like this? (filtered by forbidden header names):

const wt = new WebTransport(url, {headers: {foo: 'bar'}});
const response = await wt.ready;
for (const [name, value] of response.headers.entries()) {
  ...
}

@jan-ivar
Copy link
Member

It strikes me as a mistake to not include cookies or user-agent supplied HTTP authentication data.

@annevk do I understand your point to be that a new API for application-provided headers might not be needed if we had cookies and user-agent supplied HTTP authentication data?

@annevk
Copy link
Member

annevk commented Jan 24, 2025

No, that's incorrect. It would lessen the demand (especially for same-site scenarios) and you'd be less inconsistent with the rest of the web platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Discuss at next meeting Flags an issue to be discussed at the next WG working
Projects
None yet
Development

No branches or pull requests