cloudflare / quiche

🥧 Savoury implementation of the QUIC transport protocol and HTTP/3
https://docs.quic.tech/quiche/
BSD 2-Clause "Simplified" License
9.51k stars 723 forks source link

HTTP/3 incorect headers handling (according to RFC): #838

Open qjavax opened 3 years ago

qjavax commented 3 years ago

Hi, i discovered that quiche implementation doesn't entirely follows rfc in terms of headers: Upgrade, Transfer-encoding: chunked, modifying Content-length header. RFC says: Transfer-encoding: chunked

   The "chunked" transfer encoding defined in Section 7.1 of [HTTP11]
   MUST NOT be used.

...

The only exception to this is the TE header field, which MAY be
   present in an HTTP/3 request header; when it is, it MUST NOT contain
   any value other than "trailers".

Content-Length:

A request or response that includes a payload body can include a
   Content-Length header field.  A request or response is also malformed
   if the value of a content-length header field does not equal the sum of the DATA frame payload lengths that form the body

Upgrade:

HTTP/3 does not support the HTTP Upgrade mechanism (Section 9.9 of
   [HTTP11]) or 101 (Switching Protocols) informational status code
   (Section 9.2.2 of [SEMANTICS]).

I created comparation table of google.com cloudflare-quic.com and my server with nginx+quic (quic 0.7.0) responses:

  Upgrade: (http3) Upgrade: (http1.1) Transfer-encoding: (http3) Transfer-encoding: (http1.1) Content-length >: (http3) Content-length >: (http1.1) Content-length <: (http3) Content-length <: (http1.1) Content-length =: (http3) Content-length =: (http1.1) Content-length xxx: (http3) Content-length xxx: (http1.1)
google.com HTTP/3 400 HTTP/1.1 200 OK hangs HTTP/1.1 405 Method Not Allowed HTTP/3 400 hangs HTTP/3 400 HTTP/1.1 405 Method Not Allowed HTTP/3 405 HTTP/1.1 405 Method Not Allowed HTTP/3 400 Error 400 (Bad Request)
cloudflare-quic.com HTTP/3 200 HTTP/1.1 200 OK hangs HTTP/1.1 200 OK HTTP/3 400 hangs HTTP/3 400 HTTP/1.1 200 OK HTTP/3 200 HTTP/1.1 200 OK HTTP/3 400 HTTP/1.1 400 Bad Request
my_domain HTTP/3 200 HTTP/1.1 200 OK hangs HTTP/1.1 200 OK hangs hangs hangs HTTP/1.1 200 OK HTTP/3 200 HTTP/1.1 200 OK HTTP/3 400 HTTP/1.1 400 Bad Request

in this table let's assume google.com response 405 is 200/OK response. Upgrade: google.com response is 400 Bad request, but Quiche implementation ignores this header. (is it ok with rfc?) Transfer-encoding: hangs on all implementations, but rfc says it should be treated as malwared request so I belive server response should be 400. Content-length with greater value is treated as bad request in google.com and cloudflare-quic.com server, but only on my nginx+quic server it will hang (is it nginx patch issue?) Content-length with smaller value is treated as bad request only in google implementation. Quiche implementation truncates data frame, but it should be treated as bad request.

How i tested this:

curl --http3 -vvv https://<domain>/  -H "Connection: Upgrade;" -H "Upgrade: h2c"
curl --http3 -vvv https://<domain>/ -H "Content-Type: application/x-www-form-urlencoded" -H "Transfer-encoding: chunked" --data "test"
curl --http3 -vvv https://<domain>/ -H "Content-Length: 10" --data "test"
curl --http3 -vvv https://<domain>/ -H "Content-Length: 1" --data "test"
curl --http3 -vvv https://<domain>/ -H "Content-Length: 4" --data "test"
curl --http3 -vvv https://<domain>/ -H "Content-Length: xxx" --data "test"

Last interesting note about content-length: when cloudflare-quic.com gets malwared content-length header will return different responses, i.e:

> content-length: 1
> content-type: application/x-www-form-urlencoded
> 
} [4 bytes data]
< HTTP/3 400
< server: nginx
< date: Thu, 04 Feb 2021 10:38:43 GMT
< content-type: text/html
< content-length: 155
< cf-ray: 61c3c37eddd2166d-WAW
< 

but correct content-length response is:

> content-length: 4
> content-type: application/x-www-form-urlencoded
> 
} [4 bytes data]
< HTTP/3 200
< date: Thu, 04 Feb 2021 10:39:16 GMT
< content-type: text/html
< content-length: 109425
< set-cookie: __cfduid=dbbf02f7232794bf8d7c19597e7f3d9a71612435156; expires=Sat, 06-Mar-21 10:39:16 GMT; path=/; domain=.cloudflare-quic.com; HttpOnly; SameSite=Lax; Secure
< cf-request-id: 080e39072a00001661e609c000000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< server: cloudflare
< cf-ray: 61c3c451dc241661-WAW
< alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
< 

My configuration:

curl 7.75.0-DEV (x86_64-pc-linux-gnu) libcurl/7.75.0-DEV BoringSSL zlib/1.2.11 libidn2/2.0.5 quiche/0.7.0
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp 
Features: alt-svc AsynchDNS HTTP3 HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB SSL UnixSockets

Nginx 1.16.1 with latest quiche patch.

LPardue commented 3 years ago

Hi @qjavax , thanks for the detailed report! We'll look into things.

qjavax commented 3 years ago

@LPardue hi, do you have any information about this topic already?