Skip to content

Settings → Wi-Fi: redact password from serial/REPL logs#124

Merged
ThomasFarstrike merged 1 commit into
MicroPythonOS:mainfrom
bitcoin3us:security/wifi-password-leak
Apr 23, 2026
Merged

Settings → Wi-Fi: redact password from serial/REPL logs#124
ThomasFarstrike merged 1 commit into
MicroPythonOS:mainfrom
bitcoin3us:security/wifi-password-leak

Conversation

@bitcoin3us
Copy link
Copy Markdown
Contributor

Summary

Three print sites in builtin/apps/com.micropythonos.settings.wifi/assets/wifi_settings.py were dumping the Wi-Fi password to the serial console / REPL on every Wi-Fi interaction:

  1. edit_network_result_callback (line 158):

    print(f"EditNetwork finished, result: {result}")

    result is {'result_code': True, 'data': {'ssid': '…', 'password': '…', 'hidden': …}} — fires every time the user saves the EditNetwork screen.

  2. start_attempt_connecting (line 175):

    print(f"start_attempt_connecting: Attempting to connect to SSID '{ssid}' with password '{password}'")

    Fires on every connection attempt.

  3. gotqr_result_callback (lines 395 and 398):

    print(f"QR capture finished, result: {result}")
    # ...
    print(f"Setting textarea data: {data}")

    A Wi-Fi QR code's payload is the standard WIFI:T:WPA;S:SSID;P:password;H:…;; format — the password is in plaintext. Both result and data contain it. Fires every time a Wi-Fi QR is scanned.

Discovery

Spotted while testing the LightningPiggyApp's Wi-Fi reconnect flow. The device printed the user's real home Wi-Fi password to serial on every connection retry. Same class of leak as #123 (SharedPreferences.load) but at the app layer.

Fix

  • edit_network_result_callback: shallow-copy the result and replace the nested data.password with *** before printing.
  • start_attempt_connecting: log only the SSID; drop the password from the print.
  • gotqr_result_callback: replace the two raw-content prints with a minimal status print (just result_code).

All three edits include a comment explaining why so future edits don't accidentally regress.

No behaviour change

  • No API change
  • No control flow change
  • No test impact
  • Only log output

CHANGELOG

Added a bullet under the new Builtin Apps: subsection of the "Future release" block.

Release target

Security fix, suggest merging to main for inclusion in the next release regardless of its feature scope. If you'd rather fold it into a patch release, happy to rebase.

Test plan

  • Open Settings → Wi-Fi → select an SSID, enter a password, Save. Verify REPL shows EditNetwork finished, result: {'result_code': True, 'data': {'ssid': '…', 'password': '***', 'hidden': …}}
  • Same flow triggers connection — verify REPL shows start_attempt_connecting: Attempting to connect to SSID 'X' with no password
  • Scan a Wi-Fi QR code — verify REPL shows only QR capture finished, result_code=True and no raw WIFI:…;P:password;; string

🤖 Generated with Claude Code

Three print sites in the Settings → Wi-Fi app were leaking the
Wi-Fi password to the serial console / REPL:

1. `edit_network_result_callback` — prints `result` which is
   `{'result_code': True, 'data': {'ssid': ..., 'password': ...,
   'hidden': ...}}`. Fires every time the user saves the
   EditNetwork screen.
2. `start_attempt_connecting` — prints `'... SSID 'X' with
   password 'Y''`. Fires on every connection attempt.
3. `gotqr_result_callback` — prints the raw result and the raw
   QR data. A Wi-Fi QR code's payload is
   `WIFI:T:WPA;S:SSID;P:password;H:...;;` — the password is in
   plaintext. Fires every time a Wi-Fi QR is scanned.

Discovered while testing LightningPiggy app's Wi-Fi reconnect
flow on a device: the user's home Wi-Fi password was printed to
serial on every connect retry, and would appear in any shared
debug log or REPL paste. Companion to MicroPythonOS#123 (which
fixed the `SharedPreferences.load()` leak in the same vein).

Fix: redact `password` from the result-dict dump, drop the
password argument from the connect-attempt print, and drop both
the full-result and raw-QR prints from the QR callback (replaced
with a minimal status print).

No API change, no behaviour change — only log output.
@jedie
Copy link
Copy Markdown
Contributor

jedie commented Apr 20, 2026

I don't know if this is really needed. Sadly the WiFi password is always accessible in plain text if you have physical access to the ESP. There is IMHO no way to secure this.

There is Flash/NVS (Non-Volatile Storage) encryption, but it's not supported by micropython out-of-the-box, i think.

@bitcoin3us
Copy link
Copy Markdown
Contributor Author

Fair point on flash/NVS — physical access to the chip is a separate threat model that a log-redaction change can't (and isn't trying to) address. Flash encryption would be the right lever there, and you're right that MicroPython doesn't turn it on by default.

The vector this PR is closing is different: secrets leaking through logs, not storage. The affected print sites dump the Wi-Fi password to the serial console / REPL on every connect attempt, every EditNetwork save, every Wi-Fi QR scan. That output reaches a much wider audience than anyone with a chip in hand:

  • Developers and users watching serial during normal operation
  • Anyone on a remote mpremote / WebREPL session (no physical access needed)
  • Diagnostic output copy-pasted into GitHub issues, forum posts, Discord
  • CI / automated test logs that capture serial streams
  • AI coding assistants (Claude Code, etc.) whose monitors pipe serial output into context they then serialise to the cloud

I actually spotted this while debugging the LightningPiggyApp with Claude Code — the agent's serial monitor captured my home Wi-Fi password in its transcript on every reconnect. That's the kind of failure mode log-redaction fixes and flash encryption wouldn't touch.

The fix is also zero-risk: no API change, no control-flow change, just replacing the leaked field with *** in the log string. So even as a defence-in-depth measure alongside a future flash-encryption discussion, the cost/benefit looks good.

Happy to discuss further if you'd still rather not take it.

@ThomasFarstrike
Copy link
Copy Markdown
Contributor

Thanks for both of your thorough considerations, guys! I understand we've reached a loose consensus on the merit of this change so let's merge it!

@ThomasFarstrike ThomasFarstrike merged commit 323955d into MicroPythonOS:main Apr 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants