JWS: signing HTTP headers

Looking over the spec for JSON Web Signature / JWS https://tools.ietf.org/html/rfc7515, I realise that it doesn’t seem to specify a method for signing HTTP headers.

Is there some way to use JWS to verify that HTTP headers were set by the same party that signed the JWS payload? [Or perhaps, suggest an alternative mechanism]

Adding Expect-CT header to HTTP response

In the security test report, I have a recommendation to add Expect-CT header to the HTTP response from web application, additionally developers set this to:

Expect-CT: max-age=0, report-uri=

I am not sure if it is a good idea to add this header. According to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT:

“The Expect-CT will likely become obsolete in June 2021. Since May 2018 new certificates are expected to support SCTs by default. Certificates before March 2018 were allowed to have a lifetime of 39 months, those will all be expired in June 2021.”

So because certificates are expected to support SCTs by default I do not think that this header makes any sense.

When it comes to configuration according to https://scotthelme.co.uk/a-new-security-header-expect-ct/ max-age=0, report-uri= means:

“This policy is deployed in report-only mode and if the browser doesn’t receive CT information that it’s happy with, referred to as not being ‘CT Qualified’, rather than terminate the connection it will simply send a report to the specified report-uri value.”

Because I don’t have uri here, the report will not be sent, so there is no additional security at all.

On the other hand I see that some popular websites like Linkedin still use this header, the example from Linkedin:

Expect-CT: max-age=86400, report-uri="https://www.linkedin.com/platform-telemetry/ct"

Python Script POST Body Containing CRLF Characters and Malformed Headers. HTTP Request Smuggling

Lately I have been attempting Portswiggers WebSecAcademy’s HTTP request smuggling labs with the additional challenge of writing a python script to complete the challenge for me.

Intended solution from Burp Repeater:

POST / HTTP/1.1 Host: ac971f2f1fe48ec180f863d5009000ed.web-security-academy.net User-Agent: Mozilla/5.0 (X11; Linux i686; rv:68.0) Gecko/20100101 Firefox/68.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te Connection: close Upgrade-Insecure-Requests: 1 Content-Length: 10 Transfer-Encoding: chunked  0  G  

If you right click and select ‘Copy as curl command’:

curl -i -s -k -X $  'POST' \     -H $  'Host: ac011f9b1f7e242780ce2272008a009d.web-security-academy.net' -H $  'User-Agent: Mozilla/5.0 (X11; Linux i686; rv:68.0) Gecko/20100101 Firefox/68.0' -H $  'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H $  'Accept-Language: en-US,en;q=0.5' -H $  'Accept-Encoding: gzip, deflate' -H $  'Referer: https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te' -H $  'Connection: close' -H $  'Upgrade-Insecure-Requests: 1' -H $  'Content-Length: 8' \     --data-binary $  '0\x0d\x0a\x0d\x0aG\x0d\x0a\x0d\x0a' \     $  'https://ac011f9b1f7e242780ce2272008a009d.web-security-academy.net/' 

When attempting this with Curl, it returns 500 internal server error.

I have managed to complete this using the Python requests module:

def POST_CLTE():     url = 'https://ac011f9b1f7e242780ce2272008a009d.web-security-academy.net/'     headers = {'Host':'ac011f9b1f7e242780ce2272008a009d.web-security-academy.net','Connection':'keep-alive',     'Content-Type':'application/x-www-form-urlencoded','Content-Length':'8', 'Transfer-Encoding':'chunked'}      data = '0\x0d\x0a\x0d\x0aG\x0d\x0a'      s = requests.Session()     r = requests.Request('POST', url, headers=headers, data=data)     prepared = r.prepare()     response = s.send(prepared)      print(response.request.headers)     print(response.status_code)     print(response.text) 

But I don’t like that I have to pass the header in as a dict and it complains when I want to include an obfuscated header such as:

X: X[\n]Transfer-Encoding: chunked 

I’ve attempted to reproduce the request using PyCurl:

#!/usr/bin/python  import pycurl from StringIO import StringIO  buffer = StringIO() c = pycurl.Curl() c.setopt(c.POST, 1) c.setopt(c.URL, 'https://ac011f9b1f7e242780ce2272008a009d.web-security-academy.net/') c.setopt(c.POSTFIELDS, '0\x0d\x0a\x0d\x0aG\x0d\x0a') #c.setopt(pycurl.POSTFIELDSIZE, 8) c.setopt(c.HTTPHEADER, [     'User-Agent: Mozilla/5.0 (X11; Linux i686; rv:68.0) Gecko/20100101 Firefox/68.0',     'Host: ac011f9b1f7e242780ce2272008a009d.web-security-academy.net',     'Content-Length: 8',     'Transfer-Encoding: chunked',     'Content-Type: application/x-www-form-urlencoded'     ]) #c.setopt(c.CRLF, 1) c.setopt(c.VERBOSE, 1) c.setopt(c.HEADER, 1) c.setopt(c.WRITEDATA, buffer) c.perform() c.close()  body = buffer.getvalue()  print(body) 

I like that I can pass the headers as an array of strings, but I unfortunately still get 500 internal server error:

*   Trying 18.200.141.238:443...                                                                                                                             * TCP_NODELAY set                                                                                                                                            * Connected to ac561fd21ed819768081009200f2002e.web-security-academy.net (18.200.141.238) port 443 (#0)                                                      * found 387 certificates in /etc/ssl/certs * ALPN, offering h2 * ALPN, offering http/1.1 * SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256 *        server certificate verification OK *        server certificate status verification SKIPPED *        common name: web-security-academy.net (matched) *        server certificate expiration date OK *        server certificate activation date OK *        certificate public key: RSA *        certificate version: #3 *        subject: CN=web-security-academy.net *        start date: Fri, 05 Jul 2019 00:00:00 GMT *        expire date: Wed, 05 Aug 2020 12:00:00 GMT *        issuer: C=US,O=Amazon,OU=Server CA 1B,CN=Amazon * ALPN, server did not agree to a protocol > POST / HTTP/1.1 Host: ac561fd21ed819768081009200f2002e.web-security-academy.net Accept: */* User-Agent: Mozilla/5.0 (X11; Linux i686; rv:68.0) Gecko/20100101 Firefox/68.0  Content-Length: 8 Transfer-Encoding: chunked Content-Type: application/x-www-form-urlencoded  8 * upload completely sent off: 15 out of 8 bytes * Mark bundle as not supporting multiuse < HTTP/1.1 500 Internal Server Error < Content-Type: application/json; charset=utf-8 < Connection: close < Content-Length: 23 <  * Closing connection 0 HTTP/1.1 500 Internal Server Error Content-Type: application/json; charset=utf-8 Connection: close Content-Length: 23  "Internal Server Error" 

What is the reason for this behaviour? Are there any alternatives I haven’t explored? Any suggestions are much appreciated.

Is there a way to limit cookies to certain hosts in HTTP?

Using Nginx, I hope to restrict the permissible hosts for cookies. My initial intention was to employ a Content Security Policy for this purpose, but I don’t see an obvious way to do this via a CSP. Ideally I’d find something like

Restrict-Cookies-Header: hostname1.tld hostname2.tld2 

Can something like this be accomplished with HTTP headers? Thanks!

HTTP Public Key Pinning vs Certificate Transparency, which is better and why?

We are rolling out a new mobile app. Our security team recommends us to pin the public key in order to avoid MITM. iOS already has CT checks and we can enable that for the Android app as well.

The security team’s arguments for pinning are:

  1. Pinning is a more hardened solution
  2. CT is more reactive, we might have to live with a rogue certificate until it is revoked and logged in CT logs
  3. CT checks would not work in environments where corporate assets may have company PKI deployed and MITM attack is done using the company PKI

My argument for not pinning: Assume we generate a certificate and it is a 2048-bit RSA key pair. The certificate expires in 1 year, maybe 2. We go to the CA for a new certificate and the CA says “No, your key is too small, come back with a 4096 RSA key pair”. In effect, because we pinned the public key in the app, the app is bricked.

Now we release a new version of the app, but it takes typically > 1 month for the app to be adopted by all the customers. During this time, while our customers are trying to access the app at home, trying to look for offers to come and spend money in our ecosystem, they won’t be able to use the app and hence we stand to lose the customer and potential revenue.

A rogue certificate might affect 1, maybe 2 customers if someone is really being targeted to be hacked (MITM target), but the above risk is a risk to the entire customer base and for amount of time that is not directly quantifiable.

Does it really makes sense to pin? If yes, how do we mitigate the above scenario? Am I not seeing something?

Does signing of HTTP requests have any benefit when using TLS?

Signing HTTP requests is all the rage nowadays. The benefits of doing so when communicating over an insecure channel are clear. With signatures you can bring message integrity and authentication to such an environment.

But I’m struggling what signatures bring to the table when the communication channel IS secure. Like TLS. Message integrity is guaranteed by TLS and authentication could be a much simpler shared secret (like an API key).

The only thing I can come up with is a niche scenarios in which sharing secrets is not desirable or exchanging the secret needs to happen on an insecure channel. Perhaps it is security in depth which merits it?