TLS + SNI -- How to Break HTTPS on Android 7.0 Nougat

nginx
#1

I use the Discourse Android app to access talk.libreho.st on my Nougat device. For some reason, when I first tried to add it to the app, I was receiving an error: talk.libreho.st not found!.

Other people tried and succeeded to add this site to their app. I was confused. Discourse team member Falco figured out the culprit: Android 7.0 had a regression that forces Nginx servers that want to provide service for it to downgrade their key exchange mechanism and use ssl_ecdh_curve prime256v1.

Of course, I did it, but I was still getting the error. In order to debug and ensure it was not a matter of Nginx version, I matched known existing configurations: I upgraded the Nginx version, and removed the warnings relative to ignored redundant default server configuration _: everything was the same on succeeding servers and the failing one. I was puzzled.

Then I noticed something: SSLLabs test would not present the right certificate that I would get with curl or with Firefox, but the certificate for lab.libreho.st: here we go! The configuration for this virtual host used ssl_ecdh_curve secp384r1. As soon as I put ssl_ecdh_curve prime256v1, my Discourse app recognized talk.libreho.st as a valid Discourse instance!


I also had to bump proxy_buffer_size to 8K on the frontend to make SAML login work with the mobile. Hackety hackety hack!


What happened?

Server Name Indication ( SNI ) is an extension to the TLS computer networking protocol by which a client indicates which hostname it is attempting to connect to at the start of the handshaking process.[1] This allows a server to present multiple certificates on the same IP address and TCP port number and hence allows multiple secure HTTPS websites (or any other service over TLS) to be served by the same IP address without requiring all those sites to use the same certificate. It is the conceptual equivalent to HTTP/1.1 name-based virtual hosting, but for HTTPS.[2]

I did not know, but apparently one of the consequences of broken Android 7.0 version of the Discourse app is that it does not take into account the “hostname it is attempted to connect to” as the reference for the success of adding the Discourse instance, but the hostname from the certificate it receives, which matches the first hostname served over HTTPS in alphabetical order!. That means the bug was due to an incorrect interpretation of the hostname by the failing Android TLS operation. As the Discourse app uses Chrome in the background, I suppose the error lies there and therefore cannot be fixed in the app itself for this Android version.

Bottomline: when you encounter a weird issue with SNI (secure site not working as intended), check that your first virtual host in alphabetical order is configured as you wish other hosts are configured! The current version of our Nginx setup does that, but as TLS technologies evolve, client operating systems become obsolete – a terrible perspective in terms of ecological footprint of mobile technologies. This demonstrates that the battle for software freedom is strongly tied to the ability we have to perform upgrades on our hardware. Currently, this is not the case, and each year produces tons of obsolete, but otherwise perfectly fine, hardware-bundled-with-software.


  1. https://en.wikipedia.org/wiki/Server_Name_Indication#cite_note-rfc3546-1 ↩︎

  2. https://en.wikipedia.org/wiki/Server_Name_Indication ↩︎

1 Like