Why TLS is better without STARTTLS (2021)
nostarttls.secvuln.info
nostarttls.secvuln.info
Once I read about it, I tried attacking my own server by sending in "STLS\r\nCAPA\r\n" in a single TCP package and it responded to CAPA after the encrypted channel had been established because those bytes were still in the buffer.
One hasty bugfix to clear the buffers after establishing TLS later and I thought about how to exploit this bug. The only thing you can do at this point is log in and an attacker isn't going to know the user's credentials. (If they do, you don't need to exploit the bug.)
Despite this, I wonder about what what if HTTP had a STARTTLS command instead of using port 443 and handing over directly to TLS. SNI (identifying the names host the client wishes to connect to prior to certificate exchange) took years to get established, mostly (I think) because it was inside the black-box of TLS. If the certificate exchange happened with HTTP before handing over to TLS, the request might have looked like:
GET /.well-known/tls-cert HTTP/1.1
Host: the-actual-hostname
Accept: certificate/format, et/c
When the hand-over to TLS (in this parallel universe) takes place, the certificate exchange has already happened. TLS's job is now to confirm the cert is valid (or not) and exchange session keys.It's an unfortunate accident of history that encryption was tied to some application protocols, and is not part of the standard TCP/IP stack provided at the OS level entirely. This same accident led to bad ideas like STARTTLS, unfortunately.
https://github.com/stolendata/little-peepo/blob/master/littl...
As soon as you make a serious breaking change, it brings out all the complaints of "centralisation" of "being forced into something" into "I don't need tls whatever you say" etc.
Google try these sorts of things occasionally but although in most cases it is short-term pain for long-term gain, it rarely goes that way.
Discarding bytes seems like the wrong thing to do as well. If your starttls just takes the socket and no "initial bytes", as I would expect, you would need to either read off the socket a byte at a time til then or better to use MSG_PEEK to not read past the next \r\n.
I read the relevant RFC and there was a clear exception to the rules of PIPELINING that meant a conforming client doesn't send the "ClientHello" until the server has responded to the STLS command. I felt justified in clearing the buffer at that point.
A couple of years ago we were alerted to the fact that iOS devices no longer connected to our LDAPS server running on port 636. macOS still seemed to connect fine..
After some digging, it seemed Apple updated iOS to only accept connecting over STARTTLS on port 389. So we had to run a STARTTLS server also to make iOS devices work.
At the time, it seemed STARTTLS over 389 was the "standard", maybe that's why Apple changed it, but looking just now it seems that is being challenged [1][2], and rightly so.
[1] https://unix.stackexchange.com/questions/607560/why-is-ldap-...
[2] https://lists.openldap.org/hyperkitty/list/openldap-technica...
... does it not? (I really don't know, I've never had to admin postgres myself)
from https://www.postgresql.org/docs/current/ssl-tcp.html#SSL-SET...
> With SSL support compiled in, the PostgreSQL server can be started with support for encrypted connections using TLS protocols enabled by setting the parameter ssl to on in postgresql.conf. The server will listen for both normal and SSL connections on the same TCP port, and will negotiate with any connecting client on whether to use SSL. By default, this is at the client's option
wait so.. if it uses the same port for TLS, does it use STARTTLS?
from https://www.postgresql.org/docs/current/protocol-flow.html#i...
> To initiate an SSL-encrypted connection, the frontend initially sends an SSLRequest message rather than a StartupMessage. The server then responds with a single byte containing S or N, indicating that it is willing or unwilling to perform SSL, respectively. The frontend might close the connection at this point if it is dissatisfied with the response. To continue after S, perform an SSL startup handshake (not described here, part of the SSL specification) with the server. If this is successful, continue with sending the usual StartupMessage. In this case the StartupMessage and all subsequent data will be SSL-encrypted. To continue after N, send the usual StartupMessage and proceed without encryption.
and also from that section
> While the protocol itself does not provide a way for the server to force SSL encryption, the administrator can configure the server to reject unencrypted sessions as a byproduct of authentication checking.
.... that is not great. I mean zero disrespect to the postgres dev team, but why do this? I know (m)TLS has issues, but why would anybody want to have to trust/verify two codebases (the postgres bits before the handoff to openssl/boringssl/whatever) instead of just one (only openssl/boringssl), just to bootstrap to a secure client-server connection?
(this is my complaint about all upgrade-to-TLS schemes)
For the server, I'm using dot-net's SslStream (in TLS mode) and it needs to know which TLS cert to use prior to handing over control of the connection.
For this reason, I've designed the protocol for the client to announce who they are and what they want as a single line of JSON before letting SslStream negotiate TLS. Once the connection is secured, the client repeats that first line of JSON. If the server detects any difference between the two it closes down the connection. (I also made sure to clear the buffers after reading that first line of JSON.)
I don't like doing that but the only other way is to "roll my own crypto" which I understand is a bad thing.
It is a bit unclear here what that means. I mean TLS has a mechanism to choose the certificate based on the hostname (SNI). Other than that - why would a different user get a different certificate? After all the certificate's job is to identify the server.
a couple examples I know of(only know go ones of the top of my head):
https://github.com/fabiolb/fabio/blob/master/proxy/tcp/tls_c... https://github.com/FiloSottile/mostly-harmless/blob/main/tal...
> I don't like doing that but the only other way is to "roll my own crypto" which I understand is a bad thing.
You would not be rolling your own cryptographic primitive, or combining existing ones in strange and speculative ways. This is what most people, most of the time mean when they admonish against rolling your own crypto.
All you'd have to do is parse the ClientHello to retrieve the SNI. you're in a memory safe language, so parsing bugs result in a crash, not a buffer overflow. I'd say you're on pretty firm ground.
All this being said, another idea (not standardized yet as far as I know) would be to publish a new DNS record, MXS (for example), that would imply the use of the SSL email port 465. I think it would be a worthy standardization effort to go in this direction and finally banish STARTTLS.
[1] https://dmarcian.com/mta-sts/ [2] https://www.rfc-editor.org/rfc/rfc7671.html