Securing ESP8266 Communication

Having a tiny device based on the ESP8266 and connecting it directly to the internet, is not a trivial task. The ‘thing’ needs to communicate somehow to at least one remote entity, and it needs to do this in a reliable way.
Regular plain communication, like TCP, HTTP or WebSockets might work in a sandboxed environment, localised on a single monitored and protected network. However, if only one single byte needs to escape this comfort zone, then all precautions need to be taken.

NodeMCU 1.0
NodeMCU 1.0

There are several problems that need to be accounted for in order to secure the communication. Out of these, three will be handled in this article:

  1. End-to-end encrypted transmission
  2. Remote server identity
  3. Local device identity

One way to handle device identity is to flash a generated client key and certificate alongside with the sketch, and use them to authenticate against the server. However, this aproach is rather bulky, and hard to implement when provisining multiple devices.
A simpler option is to generate a unique set of device address/keys and implement safe storing and mapping mechanism:

  • the device stores its unique adress either in memory (if always on), hardcoded in the sketch, flashed as data, or stored in the EEPROM (a bit insecure, because it can easily be read). For additional security, the final device address can be interleaved with pieces of the ESP8266 hardware adress.
  • the remote server contains a mapper which maps a single device address to a unique device id, in this case maybe even human readable. This is required if the device owner needs to do some registration or administration, so he/she should not know the ‘physical id’ of the device. Also with this, the human factor of exposing the device adress is eliminated.

For the other two problems, the end-to-end encryption and remote server identity, the Arduino port for ESP8266 has a nice secure client library called WiFiClientSecure. It’s not really a state of the art, but if used properly, it will do the job just right.

The WiFiClientSecure uses a TLS implementation which is based on the axTLS library. It supports TLS 1.2, so generally a default up to date server configuration will work.
However, the ESP8266 is still a limited embedded device, and the library itself is also constrained. In order to properly use it, please be aware of the following:

  • the device can’t store and process trusted CAs in order to verify the server certificate. In order to do so, the SHA1 fingerprint is only saved and compared.
  • the axTLS library supports only the following cypher suites: TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256 and TLS_RSA_WITH_AES_256_CBC_SHA256
  • the library can also be a bit buggy of the certificate is too big, or if you’re dealing with big payloads
  • the server configuration needs to be as precise, complete and simple as possible, meaning full correct configuration with no redirects and no rewrites

Getting the pieces together

1. Server configuration (apache httpd)

In your apache.conf, you need to map port 443, enable SSL, and bind the certificate(s):

2. Check the configuration

Go to and scan your server.
In the results, first check the following sections:

  • Server Key And Certificate. They should all be valid, up to date, and the name of the certificate pointing to your domain/server name. Check the RSA KeySize as well, 2048bits works just fine, and according to the docs, axTLS can handle 4096 as well
  • In the configuration section, see if TLS 1.2 is there (TLS 1.0 and 1.1 will work too) and from the list of Cipher Suites search for the supported ones listed above. At least one of them should be present.
  • Find the Incorrect SNI alerts entry in the Protocol Details sections. It must not show any problem at all.

3. Extract the fingerprint

With Chrome:

  1. Open your website (use https if you use both HTTP/HTTPS)
  2. Open DeveloperTools
  3. Go to the security tab and click on View certificate
  4. Expand the details and go to the bottom to see the SHA-1 fingerprint

4. Client implementation

The Arduino code is rather straight forward, and a sample is included in the library examples. If you can’t find it in the examples menu, you can check it out from github:
For your implementation, adapt the server hostname, the location(s) to your resources and of-course the logic.
Since in most of the cases, the devices will periodically communicate to the server, you can instantiate the WiFiClientSecure object globally and reuse it at every loop.

5*. Debug (if needed)

If something goes wrong, simple tracing and logging throughout the code will not help you a lot. When the SSL comm breaks or fails, a little is shown directly. One layer of additional debugging output can be achieved by executing “Serial.setDebugOutput(true);” after initializing the Serial object in setup(). But most often, that will not be enough.
In order to get all the details, you need to enable full debugging. If you use a NodeMCU as a development board, and you have it selected in the Arduino, then the debugging option will not be visible.
To enable full debugging for the NodeMCU, follow these steps:

  1. from the boards menu, select Generic ESP8266 Module
  2. from the freshly appeared options, find Flash Size, and choose 4M (3M SPIFFS)
  3. select nodemcu as Reset Method
  4. from Debug Port choose Serial
  5. from Debug Level choose core + ssl (or even + TLS mem)

Re-flash your sketch and open Serial Monitor. You should now see a lot more info.

At the end, again, if something goes wrong, rarely a direct error message will appear. My suggestion here is, if you’re up to date with your Arduino SDK installation and board libraries, inspect in details your server settings, because there is not much you can do wrong on the client side.