The problem can be divided into two areas dependent on where the attacker-controllable variable is: the headers and the body. Ideas for discussion:
Headers (only applicable where header compression is even possible - i.e. HTTP/2 and above):
One option is just to never compress headers; they tend to be small compared with the body (and if not, the request is probably quite small and not that worth compressing!) - fine, that's what we're already doing. Alternatively, what about adding a random-length nonce to defeat the oracle?
Calculate SHA256 sum of all the other headers plus an ephemeral server salt (static for at least the lifetime of the server process and preferably also shared across all members of the cluster of webservers if applicable, to frustrate statistical nullification of the nonce). Take the first x bits of the sum (say 4) and insert that many bytes of the end of the sum as the nonce as the final header. Now we have an incompressible string of random length combatting the attacker's attempt to elucidate the secret by checking the length of the compressed & encrypted stream while altering their controllable content.
For example, say the salted hash of the other headers is 0xa647...1f93c430377b458ebf339b6, the first 4 bits are 0xa, so for this request/response we will insert the last 0xa = 10 bytes as nonce header: 'Nonce: 3c430377b458ebf339b6'.
Cons: Depending on the size of the secrets being protected, a size range like 4 bits (0 to 15 bytes) may be statistically insufficient to prevent attacks, so this would have to be a knob to tweak in the server. If using larger max lengths, would need to securely extend the SHAsum (or switch to a variable-output-length hashing algorithm like SHA3) to give sufficient bytes for the nonce.
Body (as altered by the webserver or browser):
The body is more problematic, as there's lots of different content types, but at least we can use declared mime types to decide whether & how to handle each type. Could follow the example of the above idea for headers and insert a sum-dependent-length nonce based on the body content - e.g. __http_post_nonce=3c430377b458ebf339b6 in a POST request, an HTML body tag <http_nonce>3c430377b458ebf339b6</http_nonce>, a js comment #__js_nonce=3c430377b458ebf339b6, etc.
Cons: Only works for content types the webserver can safely insert a non-functional tag or comment into.
Body (as altered by the service generating the content):
If we drop down from the webserver to the service layer, could insert a safety mechanism (like how you otherwise sanitise user inputs like SQL with escaping) around all user-modifiable content. The mechanism tells the webserver layer to include the specified content without compressing it - e.g. mysecretlogincookie=__http_no_compress=291e36ab... in a POST request, #__js_nocompress on the end of a line containing a secret in javascript, <http_nocompress><p>Email address: attacker-controlled@example.com</p></http_nocompress> in HTML. The webserver at the layer above would then need a mechanism to chunk compressed and uncompressed sections together in a way the other side could understand. Tar? lol.
Cons: Requires support both in the webserver (or browser) and whatever service is generating the content. Whether the mechanism is used per-request could be negotiated with modified existing Accept-Encoding header - e.g. Accept-Encoding: gzip-safe, br-safe.
So, HN, (sh|c)ould it be done? Is it worth it? Is there ongoing work in this area that I'm unaware of, or have we collectively given up on compressing SSL content?