Apart from the fact that TLS doesn't have "connection context" variables, is there any reason why this couldn't be technically built into the standard for something like JWT?
Why it is needed
When a browser sends multiple requests to the same API server, it resends the JWT in the header multiple times. This means additional data overhead, and each time, the server needs to verify the JWT.
I would like to see the TLS standard include "connection bound context variables" so that JWTs can be sent once and used across multiple HTTP REQUESTS over that TLS connection.
How it could work
1) The client connects to the server, and a TLS encrypted session is established.
2) The client send the JWT to the server-side - there are two options for this:
A) The client can send connection variables directly on the TLS layer. The client sets the variable on the client side, and TLS sends the data to the server side. The variable name from the client is always prefixed with "CLIENT-".
B) The client sends a HTTP request over the TLS session to a specific endpoint dedicated for use to set a TLS variable /api/setJWT. The client sends the JWT as a POST. The server reads the JWT, verifies it, and then sets it to the TLS connection context.
[B] would work best, because client software would be harder to change (browsers).
3)
On the serverside, the encapsulating TLS object's ITLSConnectionContext interface is made available from HTTP REQUEST handlers. The ITLSConnectionContext has three functions, string[] ListVariables(), string GetVariable(name), SetVariable(name, string).
Assuming 2B is used, when GetVariable("JWT") is successful, it can be assumed that it's valid.
How it could work without changing TLS standard
If a particular TLS connection between the server and the client has a secure identity, then the application-layer can link that to a simple dictionary lookup. Perhaps this could be a GUID (as well as the client-side IPAddress-Port tuple). This GUID approach would mean each language framework can natively implement the lookup, and that makes other benefits possible, where native memory objects can also be linked, not just platform-independent strings.
Other benefits
Furthermore, the server might variables on the context for other purposes:
1) A database PK for the Person behind the JWT, PersonID. It might be a stream interface 2) A particular stream (TCP connection) to another resource 3) A file lock to a user-related logging file
The web would be better.
But am I wrong? Am I missing something that would make this unworkable and insecure?
My rebuttal to Steffen Ullrich answer:
Thanks for your answer. A comment was going to be too small for me to fit my response to your answer.
I can see no obvious security issues apart from a potential attack of exhausting memory in the peers TLS stack this way
i) With [2B] only the server-side can add a variable to the connection state, so the client would not be able to attack the server by filling memory. Maximums for POST size and other security features would still apply.
ii) Relative to sending a JWT (say 2KB) as part of every HTTP request, I don't see much of a difference in memory usage. The peak memory usage would be 2KB for example for both the connection and the HTTP requests. In the case of individual HTTP requests, the memory is possibly stackallocated (or at least temporary).
The web application layer is fully independent of the actual transport. Two requests might share the same TLS connection and/or the same TLS session or they might be not. There is no way for the web application to find out or influence this, which means binding such storage to the TLS connection/session is unusable with the current application model.
You might be right, my idea isn't comprehensive, I was more interested in the security aspects. But if I ever built it, I would need to overcome this engineering challenge. (Communication between protocol encapsulation layers isn't a new thing)
What you propose can already be implemented at the HTTP level by associating such a storage with the session cookie. And it is actually more usable at this level since the application is already aware of the user session (but not of the TLS session).
Yes, in the scope of HTTP, this is effectively a server-side session variable with TLS-connection scope. However, I wasn't going to necessarily limit this to HTTP. Ideally the JWT could be supplied to the TLS protocol on the client side during connection initialisation, but that's a much larger jump than the smaller one I present for security discussion. I chose not to provide a "client certificate" JWT kind of thing. (Update: see below, where I describe how a TLS+JWT protocol is in fact superior to server-side session variable.
Your proposal is specific to "compression" of the HTTP header. This is already transparently implemented in HTTP/2 with HPACK so this specific use case might be no longer needed.
(Update 2020-02-12)
I read more about HPACK (and QPACK), and now I understand that it isn't "compression" but rather a shared dictionary. The JWT token would be a new entry in the dictionary. This is what I am looking for "functionally". I would expect that QUIC will eventually help to distribute QPACK dictionaries in a web farm.
How about reverse-proxies?
After answering Steffen Ullrich, I thought of the following problem:
If there is a load-balancer or reverse-proxy, the web application server only receives the decrypted. The reverse-proxy scenario is very common. CloudFlare is a great example where TLS is provided directly to the user, then another TLS session to the origin server.
As Steffen Ullrich suggested, a server-side session variable overcomes this better, although for a web farm, if the destination node changes, that node won't have the session variable state.
So the ultimate answer might lie within a fully-fledged TLS-protocol enhancement:
- A JWT that is part of the TLS handshake (but after encryption is available)
- The ability for reverse-proxies to forward such client-id information to the next TLS connection toward the origin server.
- If a TLS 0-RTT reconnection is possible, there's no need to resend the JWT. If 0-RTT fails, then a new TLS session is needed, and therefore a new handshake occurs which includes transmission and caching of JWT.
It would seem that TLS-protocol-level enhancement overcomes all functional problems, and even overcomes "cookie" or "server-side session" issues.