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 · 36 comments

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.

@jlpettersson
Copy link

What I meant was support for the corresponding to:

fetch(url, {
        headers: {
            'Authorization': `Bearer ${token}`,
        },
    })

this could perhaps be something like

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

@jan-ivar
Copy link
Member

@annevk ^

@wilaw wilaw added the Discuss at next meeting Flags an issue to be discussed at the next WG working label Feb 23, 2022
@annevk
Copy link
Member

annevk commented Feb 25, 2022

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.

@jan-ivar
Copy link
Member

jan-ivar commented Mar 2, 2022

Meeting:

  • Probably defer
  • Other (inelegant) workarounds: stick token into url.

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests