Is ALPN mandatory for server to establish TLSv1.3 connection via http/2 ("h2") protocol? - http2

I know, that if we talk about non secured connection, it is possible to establish connection via http/2 protocol without ALPN.
But what about TLS connection? In RFC said:
A client MUST send the connection preface (Section 3.5) and then MAY
immediately send HTTP/2 frames to such a server; servers can identify
these connections by the presence of the connection preface. This
only affects the establishment of HTTP/2 connections over cleartext
TCP; **implementations that support HTTP/2 over TLS MUST use protocol
negotiation in TLS [TLS-ALPN]**.
Does it mean that both server and client must use ALPN to establish connection via TLS and http2? Or there are workarounds and other options?

A compliant HTTP/2 client must send the ALPN extension for HTTP/2 over TLS.
However, what would a server do if the ALPN extension is not present?
This may happen with old clients, or non-compliant clients, or attackers.
The server could be legitimately configured to speak only HTTP/2 (for example, https://h2.domain.com), so it may assume that the protocol being spoken is h2 without the need of negotiating it via ALPN. This is an implementation/configuration choice. (Another valid choice could be to just close the connection if ALPN is absent).
The RFC also discusses the role of ALPN for cross-protocol attacks, see this section.
I think the intent of the RFC is to mandate the use of ALPN; however, a server should be prepared to receive connection attempts without ALPN, and at that point it can be configured to either close the connection or assume a default protocol, which is typically http/1.1, but could as well be h2.
Keep in mind that you may always use TLS without ALPN and perform an HTTP/1.1 to HTTP/2 upgrade request (where, like in ALPN, you declare what protocol you want to upgrade to), which would typically succeed as servers should support HTTP/1.1 to HTTP/2 upgrade.
So you would be able to speak HTTP/2 to such servers, after the upgrade, even without ALPN.
A server may assume that if ALPN is missing, the client wants to try with an HTTP/1.1 to HTTP/2 upgrade. If the server does not see the upgrade (but directly the HTTP/2 client preface), it may reply with 426 Upgrade Required (see here).

Related

Implementing websocket support for HTTP/2 proxies

I am developing an https http/2 proxy server as mentioned here:
https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#HTTPS-proxy-scheme
It is mentioned there that
HTTPS proxies using HTTP/2 can offer better performance in Chrome than a regular HTTP proxy due to higher connection limits (HTTP/1.1 proxies in Chrome are limited to 32 simultaneous connections across all domains).
But when users try to surf a website which is using websocket over raw http connection, the response contains 'Upgrade' http header which is forbidden to be used in http/2 as there is no websockets for HTTP/2.
https://daniel.haxx.se/blog/2016/06/15/no-websockets-over-http2/#:~:text=There%20is%20no%20websockets%20for%20HTTP%2F2.&text=That%20spec%20details%20how%20a,connection%20into%20a%20websockets%20connection.
The problem comes from the fact that when http1.1 traffic passes through an https proxy which implements http/2, headers must be transferred from http1.1 to http/2. Of course when the webpage is using TLS, there is no such a problem as all traffic passes through a connection made by http 'CONNECT' method. The problem occurs when the website does not use TLS.
If there is no solution to this problem, it means that HTTPS proxies should not implement HTTP/2 protocol.
As there is no websockets for HTTP/2
The link you posted is old, and since then WebSocket over HTTP/2 has been specified by RFC 8441.
The behavior of a HTTP/2 proxy is specified in RFC 7540, section 8.3.
When the client communicates with the proxy using secure HTTP/2, each HTTP/2 stream is a "tunnel" to the server.
Client and proxy communicate using secure HTTP/2, and the "tunnelling" happens because each HTTP/2 stream becomes an "infinite" request with an "infinite" response, i.e. an "infinite" series of HTTP/2 DATA frames in both directions that carry an opaque byte array payload.
The job of the proxy is to unwrap the DATA frames received by the client, and forward the byte array payload to the server, perhaps re-wrapping it into an HTTP/2 DATA frame if the communication between proxy and server is also HTTP/2 (it may be possible to optimize away the unwrapping and re-wrapping but it may be tricky -- for example the stream numbering could be different).
When a client attempts to perform WebSocket over HTTP/2, the browser will do as specified by RFC 8441, and the proxy will receive a CONNECT request with a :protocol pseudo-header, and the proxy will have to know what to do in that case, depending on what protocol it uses to communicate with the server.
What above describes what your proxy need to support when the communication with the client is HTTP/2.
If your proxy needs to support clients that speak HTTP/1.1, then you need to implement what is required for a proxy to support HTTP/1.1 and WebSocket proxying, and it's typically not difficult: the first is just HTTP/1.1 forwarding or HTTP CONNECT "tunnelling", the second is WebSocket HTTP Upgrade request forwarding followed by a "tunnel" connection or even a fully fledged WebSocket proxy.
Disclaimer, I have implemented all of the above in the Jetty Project.
You can use Jetty 10 or later to implement an HTTP/1.1, HTTP/2, or WebSocket, both client and server (and therefore a proxy). WebSocket over HTTP/2 is supported too.
Finally, to answer your last question, it is perfectly possible to have secure proxies support HTTP/2, even in presence of WebSocket.
For example, a clear-text WebSocket connection to a server starts with an HTTP upgrade request; this request will be sent, encrypted, to the proxy which will decrypt it and forward it to the server (using any protocol the server supports -- could even be WebSocket over secure HTTP/2); the server will reply with a 101 and upgrade the connection to WebSocket; the proxy will receive the 101 from the server and upgrade to a "tunnel" or to a WebSocket proxy; the proxy encrypts the 101 response and forward it to the client; the client decrypts it and upgrades the connection to WebSocket.
At this point, the client is speaking clear-text WebSocket to the server through a secure connection with the proxy (the proxy sees the clear-text WebSocket communication).
Viceversa, a secure WebSocket connection to a server starts with establishing a HTTP CONNECT tunnel through the proxy to the server; then the client will establish the secure communication with the server; then it will send the HTTP upgrade to WebSocket to the server (through the proxy tunnel).
At this point, the client is speaking secure WebSocket to the server through a secure connection with the proxy (the proxy cannot see the WebSocket communication).
The HTTP/2 variants are similar, just taking into account the HTTP/2 specific ways of "tunnelling" and transporting WebSocket.

Is there a way for an HTTP/1.1 only client to communicate with a server that seemingly defaults to HTTP/2 WITHOUT updating the client?

I am trying to connect to a server that defaults to HTTP/2 with Apach Http-Components 3.4, which is not HTTP/2 compatible. I see here that a client can request HTTP/2 from the server via the Upgrade header. Is there a way to request HTTP/1.1 from the server?
Yes, it should be the default. A server should only HTTP/2 to a client if it was previously negotiated.
Either:
through an upgrade from HTTP/1 through the HTTP Upgrade header
through protocol negotiation (ALPN) during the TLS handshake
It is theoretically possible to force HTTP/2 on both sides without negotation. This is called HTTP/2 with prior-knowledge. However that mode shouldn't be used on public servers, since it exactly causes the problems that you are having.
I would try to speak to the developers/maintainers of the server if the HTTP/2 only behavior is intended.

How does a browser know if a site supports HTTP/2?

If I type out https://http2.golang.org/ the chrome browser will automatically send the HTTP/2 request. How is this done?
Take stackoverflow for example, when the browser sends a request to stackoverflow.com, it has to do the following steps:
DNS lookup. find the ip address of stackoverflow.
TCP/IP handshake
TLS handshake.
HTTP request/response (Application Protocol).
....
TLS handshake
Regarding step3 TLS handshake, there is an nice explanation by #Oleg.
In order to inspect the detail of TCP/IP packet, You may need use some tools to capture packets. e.g. Wireshark.
Client sends ClientHello to server, which carries several things
supported cipher suite. which cipher suites do you like?
supported TLS version.
a random number.
the supported Application Protocols. e.g. HTTP/2, HTTP 1.1/ Spdy/..
...
Server responds SeverHello, which carries
chosen cipher suite.
chosen TLS version.
a random generated number
And, chosen application protocols in TLS application-layer protocol negotiation (ALPN), e.g. HTTP/2
Conclusion
HTTP2 request/response happens in step4. Before that, browser has already know whether sever support HTTP/2 through TLS handshake.
The chrome browser will only send a HTTP/1.1 Request to the website. As the website is HTTP/2 Enabled, it will send a message to the browser that it supports HTTP/2. The server upgrades the communication protocol between it and the server to HTTP/2 if it finds the browser capable of recognizing HTTP/2.
So, it is generally the server which converts a request to the HTTP/2 Connection. The browser just complies with the upgrade policy of the server.
The chrome browser displays that you have a HTTP/2 connection with the server or website, only after the server upgrades the communication protocol.
The string "h2" identifies the protocol where HTTP/2 uses Transport Layer >Security (TLS) [TLS12].
This identifier is used in the TLS application-layer protocol negotiation (ALPN) >extension [TLS-ALPN] field and in any place where HTTP/2 over TLS is identified.
If server support http2.0 browser will find that server is support http2.0 in TLS application-layer protocol negotiation.
refer link!

What if an HTTP/1.1 client talk to an HTTP/2 only server and what if an HTTP/2 client talk to an HTTP/1.1 only server?

HTTP/2 is definitely the future trend because it is now the standard of HTTP protocol. As we can see in Can I use, 70.15 percent of browsers support the HTTP/2. But HTTP/2 is so new that there are browsers that only support HTTP/1.x and there are many servers that only support HTTP/1.x. I knew that a client can use HTTP upgrade mechanism to negotiate a proper protocol to communicate with the server. For example, if the server supports HTTP/2, their communicating protocol will switch to HTTP/2, otherwise, HTTP/1.x is used. But this only applies to the situdation where the browser the clients used supports both HTTP/2 and HTTP/1.x, right?
But what if a user on a browser that only supports HTTP/1.x wants to communicate with HTTP/2 only server? Will the server ignore the request or send an error back to the user?
And what if a user on a brower that only supports HTTP/2 wants to communicate with HTTP/1.1 only server? I am thinking the process might go like this: The user sends a connecion preface to the server, the server cannot recognize the request, so the user might receive a connection error message. Is this right?
Or is there any browser that supports only HTTP/2?
It's important to take in consideration that the most implementations of HTTP/2 uses it over TLS 1.2 with ALPN protocol (Application Layer Protocol Negotiation). Thus the client just start the standard TLS connection. As the part of such communication the client sends "Client Hello" to the server with some information:
It's like: "Hi, Tom! It's Bob. I speak German, Russian and English. Let's talk a little". And the server send "Server Hello":
"Hi, Bob! I suggest to speak German or English". Then the client send one more short message "OK, then let's speak German" and he start to speak German without waiting of any response from the server:
The whole communication looks like on the picture below
Because both the client and the server start the communication just using TLS 1.2, which the both know. They start the main communication after the protocol negotiation. Thus the problem which you describe could not exist in the practice.
If a browser only supports HTTP/1.1 and the server only supports HTTP/2, they cannot communicate. The server will not recognize what the client sends (in particular there will be no connection preface, which the server treats - following the specification - as a connection error), and will close the connection.
"A browser that only supports HTTP/2" does not exist; if they support HTTP/2, they also support HTTP/1.1. But let's assume that such browser exist.
In this latter case, the server will see the connection preface and will not recognize the PRI method. What exactly the server does in this case depends on the server. It may return a 400 Bad Request, or perhaps just close the connection, or it may trigger an internal server error.
I've tried to visit a http2 only server with curl --http1.1 -i, here is what I got
HTTP/1.0 403 Forbidden
Content-Type: text/plain
Unknown ALPN Protocol, expected `h2` to be available.
If this is a HTTP request: The server was not configured with the `allowHTTP1` option or a listener for the `unknownProtocol` event.

Can I implements http/2 server without ALPN support?

In this way, the client can connect the server without ALPN and use http/2 default.
Is that possible?
Yes it is possible, but the conditions are strict.
Browsers, as of now, do not implement clear-text communication for HTTP/2, so if you use a browser the answer to your question is no: you have to deploy your server with ALPN support if you want browsers to be able to connect.
On the other hand, other clients such as Java clients may be able to connect to a server using clear-text HTTP/2, so for those type of clients the answer to your question is yes: you can deploy a HTTP/2 server without ALPN support.
The Jetty Project [disclaimer, I am a committer] implements a web server and servlet container that support both scenarios: you can enable ALPN for TLS (SSL) HTTP/2 communication so that browsers will be able to connect, and you can also enable clear-text HTTP/2 communication as explained here. See also the Jetty HTTP/2 documentation.

Resources