HTTP in local webserver: what are the risks?

I'm writing a firmware for a device connected in LAN with WiFi, using an ESP32 with the Arduino framework. It uses an HTTP webserver to allow the user to configure some settings and to control some functions.

If it was a regular machine with an O.S. of course I would go straight on HTTPS.
But for an embedded device like an ESP32, what are the risks of using an unsecure connection?

What a malicious user can actually do?

If the server is isolated to your home network (not exposed/accessible from the internet), then an attacker would have to be inside your network via another device, which may be more of an issue than the data they could exfiltrate from the ESP32.

The main issue with HTTP is the data is sent in clear text, so it is easy for an attacker to collect the data and potentially discover bugs in your API, or see sensitive data, or potentially take control of whatever the device is attached to. I would not control grandma's pacemaker via a HTTP session for instance.

If you transmit location (GPS, address), personal information, API keys for external services and such, the risk grows with the value of data.

You could at the very least secure the portal with a password to prevent unwanted access, however, if someone is listening in, the password will be visible. I'm starting a similar project (ESP32 server), I'll probably do some testing to see the performance difference between ESPAsyncWebSrv & esp32_https_server. If the performance level is ok for HTTPS, I'll use that (probably will be fine, I see the biggest bottleneck being the SD card with the HTML/JS resources).

1 Like

Thanks for your answer!
Actually, my device is connected to the Internet just to retrieve the current datetime from an NTP server.

But, even if this was not the case, anyone can open a port on its own router to access the web page from the outside.

How is it possible to "take control of a different device" using mine? I really don't understand how this is possible.

If I decide to go with HTTPS I need to buy an SSL certificate for each device, don't I?

If I decide to go with HTTPS I need to buy an SSL certificate for each device, don't I?

No, you can create one with GPG/Kleopatra, if you want you can also install it in your browser as a trusted cert (otherwise you get the red unsafe message asking you to continue, but is fine too). You only need to purchase one if you want to give others that extra layer of trust (you are verified by VeriSign for example). Your own cert is perfectly fine.

How is it possible to "take control of a different device" using mine? I really don't understand how this is possible.

Not necessarily another device, but if your ESP is controlling some hardware, vulnerabilities in your code could allow an attacker to possibly disable, modify, or destroy the equipment being controlled by it. If your device has OTA updates supported and someone can upload custom firmware, then it could be used to attack other facets of your network (highly unlikely, but is possible).

But, even if this was not the case, anyone can open a port on its own router to access the web page from the outside.

Sure, but you could also deny requests from IP's that are outside of the local subnet, or require them to enable allowing external connections in the settings/white list. I use Ngrok sometimes which gives me an external https portal.

Actually, my device is connected to the Internet just to retrieve the current datetime from an NTP server.

Yeah that is fine, your device is creating the requests, not accepting external requests.

Nothing can ever be 100% secure, it is up to the designers to decide when what they have is good enough based on the value of what the data/systems can do or access.

1 Like

All clear! Just a clarification:

Sure, but you could also deny requests from IP's that are outside of the local subnet

since most routers do a NAT, how can I understand the request IP is from the outside?
I think you're talking about something like this:

server.on("/api/foo", HTTP_GET, [this](AsyncWebServerRequest *request)
{    
    // metacode! just to have an idea
    if (request->client()->getRemoteAddress() != myLocalSubnet)
    {
        // deny the request
    }
});

Unfortunately I cannot try it right now, but I guess the "remote address" would be a private address as well (like 192.168.x.x). Hence it could be like my own subnet. Or am I wrong?

And what about this?

server.serveStatic("/", LittleFS, "/www")
      .setDefaultFile("index.html")
      .setTemplateProcessor(std::bind(&WebApp::processor, this, std::placeholders::_1));

Here I cannot check the remote IP before serving the pages.

since most routers do a NAT, how can I understand the request IP is from the outside?

I could be wrong, but from my understanding, the NAT on your router translates your internal addresses, the external request address would be untouched.

I have been setting up the SD card and wifi config on mine, so I cannot provide examples yet, however your pseudo code is basically what I'm intending. As for the serve static method it looks like you can do this in a filter. The code below is from the docs, but you could do the remote IP checking there and redirect external IP's to a 404 page or something.

// Filter callback using request host
bool filterOnHost1(AsyncWebServerRequest *request) { return request->host() == "host1"; }

// Server setup: server files in "/host1/" to requests for "host1", and files in "/www/" otherwise.
server.serveStatic("/", SPIFFS, "/host1/").setFilter(filterOnHost1);
server.serveStatic("/", SPIFFS, "/www/");

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.