12

I'm a developer and I'm implementing a socket connection with SSL (using javax.net.ssl.SSLEngine). For testing I'm opening connections to arbitrary HTTPS servers on the Internet to see how the handshake process proceeds after opening the connection and before closing the connection.

My question is about the handshake just before closing the connection.

My application sends an SSL close notify alert to the server and Java's SSLEngine then expects to receive a close notify alert back from the server. However, some servers do not send a close notify back to my application - they just close the connection.

For example, https://www.oracle.com sends the close notify as I expect it, but https://www.google.com does not - it just closes the connection after my app sends the close notify.

Is it optional for a server to respond to the client's close notify? What consequences does it have for my app if the server doesn't do this? (Can I safely ignore it if the server doesn't do this?).

(Example code on Github: jesperdj/sslclient)

13

The close notify should be sent by both client and server according to the specifications.

RFC 2246 Section:

The client and the server must share knowledge that the connection is ending in order to avoid a truncation attack. Either party may
initiate the exchange of closing messages.

close_notify This message notifies the recipient that the sender will not send any more messages on this connection. Note that as of TLS 1.1, failure to properly close a connection no longer requires that a session not be resumed. This is a change from TLS 1.0 to conform with widespread implementation practice.

Either party may initiate a close by sending a close_notify alert. Any data received after a closure alert is ignored.

Now, not all implementations follow the specifications. Everyone is shocked... I know. Look at the bolded part of that quote. They changed the spec in 1.1 because wide spread implementations do not send close_notify messages.

To ensure that your app is doing its due diligence you should always send the close_notify. The important thing for you is to ignore any other data that is sent to you except a close_notify. As far as you're concerned the connection is over. It is nice to hear this from the server, but all you need to do is drop any other data that comes along the tunnel.

As stated in the above quote, in TLS 1.1 you can still resume sessions even if one side does not perform a close_notify.

This does mean that you're susceptible to a truncation attack:

Truncation attack

A TLS truncation attack blocks a victim's account logout requests so that the user unknowingly remains logged into a web service. When the request to sign out is sent, the attacker injects an unencrypted TCP FIN message (no more data from sender) to close the connection. The server therefore doesn't receive the logout request and is unaware of the abnormal termination.

This is one good reason why most financial sites log you out if a session expires or times out.

3
  • Are you sure you cannot receive any information after sending close notify? You can receive some data unless you are synchronized with the other party at higher level. Due to network latency, the remote can send some data before receiving close notify. – v6ak May 13 '19 at 14:01
  • As the spec goes, "all data received after a closure alert is ignored". So once a side has sent the close notify it shouldn't process any more data if the other side sends it. – RoraΖ May 15 '19 at 12:57
  • Hmm, I have looked at the spec of TLS 1.2 more closely. The spec does not specify who should ignore all the messages after close_notify – the sender, the receiver or both. Maybe it is OK to wait for all the messages after sending close_notify to peer, but the spec is ambiguous there. However, since the peer that receives close_notify has to discard all the pending data, it is probably intended for both sending and receiving parties. For TLS 1.3, it is a different story, because it explicitly supports half-close semantics. (I mention 1.3 for completeness.) – v6ak May 19 '19 at 20:20
13

To complete @raz' answer, a few points:

OpenSSL, and thus implementations based on OpenSSL (clients and server), tend to no longer send close_notify messages; they just drop the connection. The main reason is that in existing Web servers and clients, connections are managed in a pool that closes them after some inactivity delay; that pool manages low-level sockets and has no notion of what SSL could be; thus, the SSL layer has no way to send (or wait for) an explicit close_notify.

One underlying reason for this design is to avoid keeping resources unduly. From the point of view of a server, an inactive client may be a dead client -- possibly, the client computer crashed or shut down more or less cleanly, or its network access just dropped. Thus, there may be nobody to receive the close_notify. Normally, TCP connections are buffered, so the server code could send the close_notify and close the connection immediately, without having to wait for an ACK from the client (ACK which will never come, the client having died). However, client death may occur at any time, including during the sending of the previous answer from the server, so the TCP buffer may be full at that time. Thus, the sending of the close_notify might stall for a long time, until the TCP layer decides that the connection is really dead (this may take hours).

By simply closing sockets directly, after some fixed inactivity delay, regardless of the connection logical state at that point (SSL or not; sending an answer or waiting for the next request), servers ensure that they avoid blocking resources for the benefice of dead clients.

The main "truncation attack", at the HTTPS level, is related to HTTP 0.9. In HTTP 0.9, each request uses a new connection, and the server sends back the answer with no explicit data termination; when the server has finished sending the data, it closes the underlying socket. The client thus knows that it got all the data when the socket is closed.

With HTTP 0.9, lack of a close_notify implies that attackers could truncated the answer (by forcing a TCP-level close) and the client would not know it. This was accounted as one of the main weaknesses of SSL 2.0.

However, opening a new connection for each request is greatly inefficient, so from HTTP 1.0 onwards, clients and server reuse connections for several requests and responses. This, in turn, requires the HTTP protocol to be self-terminated: inspection of the exchanged bytes alone must be sufficient to decide whether the end of a request or answer was reached, without relying on a "closure" event from the transport medium. Basically, clients and servers use explicit Content-Length headers, or (for data streaming) use the "chunked transfer coding". When the application protocol that goes within the SSL/TLS tunnel is self-terminated, the close_notify is redundant, and can be dispensed with. Which is exactly what happens in modern HTTPS. No modern server ever uses HTTP 0.9 any more; all their responses are self-terminated; so they can afford (from a security point of view) not to send a close_notify.

The truncation attack that @raz talks about is one level higher: the client sends an HTTP request for some "logout" action, but does not wait for the corresponding answer (an HTTP answer, not a close_notify). Presence or lack of a close_notify does not change anything here; the weakness is an impatient user.

SSL/TLS session reuse is actually larger than a mere resumption. Originally, SSL was designed under the idea that the client opened one connection to the server, simply opening a new one if the previous one was closed.

Web browser no longer do that. Instead, they open several connections to the server, mainly so that they can send requests in parallel, for a better user experience. The HTTP-1.0 reference says that:

   Clients that use persistent connections SHOULD limit the number of
   simultaneous connections that they maintain to a given server. A
   single-user client SHOULD NOT maintain more than 2 connections with
   any server or proxy.

However, practice is, as always, a bit different (some browsers, though, apply different limitations on the number of simultaneous connections depending on whether the connections use SSL or not).

For all these connections, a typical browser will do a complete handshake with one connection, then "resume" that session for other connections, even though the "session" is still alive and kicking on the first connection. Therefore, sessions can be resumed before having been stopped. The provision in TLS 1.0, quoted by @raz, merely extends that idea in that it defines that sessions can be resumed regardless of what happened or still happens with the same session on other connections. In effect, session resumption is no longer about resuming, but rather reusing the session parameters (i.e. the negotiated shared secret key).

Summary: SSL/TLS formally provides cryptographically-protected closure: when a connection is closed, both client and server have guarantees that the closure is genuine, and not injected by an interloping attacker. Existing practice deviates from that theory, and implementations no longer offer that guarantee.

This is not a problem for HTTPS Web servers because such servers enforce the use of a self-terminated application protocol (HTTP 1.0+ with explicit chunk or content lengths) that does not need cryptographic protection of closures.

In full generality, not sending a close_notify is a weakening of the protocol, which might be leveraged if (and only if) the underlying protocol is not self-terminated. Fortunately, non-self-terminated protocols are exceedingly rare, and could be described as "poorly designed" (if only because they don't allow connection reuse), so this weakening is not a big issue in practice (for the time being...).

3
  • I don't think it is true that implementations based on OpenSSL don't send close_notify. If you use SSL_shutdown the close_notify gets send. You even need this kind of proper shutdown if you implement something like the CCC command for FTPS where the SSL socket gets downgraded back to a plain socket. – Steffen Ullrich Feb 19 '15 at 20:27
  • Thank you very much for this thorough answer! I have one question: I am maintaining an application that can act as both client and server, and there is existing code that ensures that SSL_shutdown() is used when a session is closed, but only if the application acts in "client" mode. I think this is a mistake in the application (it should send shutdown also in server mode when closing the connection!), but I am asking to make sure: Using SSL_shutdown() in the server would still allow session reuse by clients that connect to this server, is that right? Thank you very much! – mat Dec 3 '16 at 19:14
  • @mat SSL_shutdown() is an OpenSSL function that runs the connection close protocol (the close_notify exchange) but does not invalidate the session. The session can be resumed. – Thomas Pornin Dec 4 '16 at 21:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.