Articles / Embedded

  • esp8266
  • arduino
  • wifi
  • flash
  • security
  • esptool

Anyone Can Read Your ESP8266's Wi-Fi Password Off the Flash

If someone picks up your ESP8266 board, they can read your Wi-Fi password from it in about a minute. No exploit, no soldering, just a USB cable and the same tool you used to flash it. The password sits in the flash as plain text, and the chip has no hardware feature to stop anyone reading it back.

This is easy to demonstrate on your own board, so let's do that first.

Dump the flash and look

You need esptool and the board plugged in. First find the flash size, then read the lot:

esptool.py --port /dev/ttyUSB0 flash_id
esptool.py --port /dev/ttyUSB0 read_flash 0 0x400000 dump.bin
strings -n 8 dump.bin | grep -i -A2 'MyHomeSSID'

The 0x400000 is a 4 MB part; use whatever flash_id reports. Newer esptool releases spell the commands with hyphens (read-flash) and the old names still work with a warning, at least in the versions I know of.

You will see your SSID and, very likely, the password right next to it. The ESP8266 has no flash encryption and no read-back protection, so the ROM bootloader will hand over every byte to anyone who holds GPIO0 low at reset.

Two places the password lives

People usually only think of one of them, which is why scrubbing the source is not enough.

  1. Your firmware image. A line like const char* ssid = "..."; puts the literal into the binary. It is stored as ordinary bytes.
  2. The SDK's saved config. By default the Arduino core calls the SDK in a mode that writes the SSID and passphrase to a flash sector when you call WiFi.begin(). This is the "persistent" behaviour, and it survives you reflashing the sketch.
  3. Any config file you add. A config.json on LittleFS or SPIFFS is a plain file inside the filesystem partition, and tools such as mklittlefs can unpack a dumped image.

That second one catches people out. You can remove the credentials from the sketch, flash the new build, and the old password is still sitting in the SDK area until something overwrites it.

Quick detour: why does the string survive?

Hang on, why does a string literal show up as readable text at all? Because the compiler has no reason to do anything else. The code is executed straight from flash via a cache, so the bytes must be in the image exactly as the CPU expects to read them. Obfuscating them would mean decoding at runtime, and the decoder and key would be in the same dump.

What actually helps

Be honest about the threat first. If the attacker has the board, the board's secrets are theirs. The goal is to make that cheap to recover from, not to make the secret unreadable.

  • Put IoT devices on their own SSID or VLAN, with no route to the machines you care about.
  • Use a separate passphrase for that network, one you never use for anything else.
  • Rotate it if a board is lost, sold or binned. That is the real fix.
  • Keep credentials out of git. A secrets.h in .gitignore stops the repository leaking it, though not the board.

Two small code changes also help. Stop the SDK writing config you did not ask for, and skip the sketch-level literal by provisioning at runtime (a captive portal, for instance):

#include <ESP8266WiFi.h>

void setup() {
  WiFi.persistent(false);  // do not write SSID/password to the SDK flash area
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);  // ssid/pass from wherever you provision them
}

Be aware that a provisioning flow still has to store the password somewhere so the device can reconnect after power loss. On an ESP8266 that somewhere is flash, so you have moved the secret rather than hidden it. The benefit is that one firmware image can be shared or published without your credentials baked in.

Before you sell or bin the board

Erase everything, not just the sketch:

esptool.py --port /dev/ttyUSB0 erase_flash

Then re-dump it and run strings again to confirm. If you want per-device secrets that cannot be pulled out, the ESP32 family has flash encryption and secure boot, which is a good reason to choose one for anything that holds a credential you care about.