Harden web UI auth, input handling, and SD path validation

- Add optional Basic Auth with NVS-backed credentials and STA/AP flags; protect status, wifi, history, and download routes

- Stop pre-filling WiFi/MQTT/Web UI password fields; keep stored secrets on blank and add clear-password checkboxes

- Add HTML escaping + URL encoding helpers and apply to user-controlled strings; add unit test

- Harden /sd/download path validation (prefix, length, dotdot, slashes) and log rejections

- Enforce protocol version in LoRa receive and release GPIO14 before SD init

- Update README security, SD, and GPIO sharing notes
This commit is contained in:
2026-02-02 21:07:37 +01:00
parent b5477262ea
commit 0e12b406de
10 changed files with 260 additions and 30 deletions

View File

@@ -31,10 +31,10 @@ Variants:
- SCL: GPIO22
- I2C address: 0x68
- Battery ADC: GPIO35 (via on-board divider)
- **Role select**: GPIO14 (INPUT_PULLDOWN, sampled at boot)
- **Role select**: GPIO14 (INPUT_PULLDOWN, sampled at boot, **shared with SD SCK**)
- HIGH = Sender
- LOW/floating = Receiver
- **OLED control**: GPIO13 (INPUT_PULLDOWN, sender only)
- **OLED control**: GPIO13 (INPUT_PULLDOWN, sender only, **shared with SD CS**)
- HIGH = force OLED on
- LOW = allow auto-off after timeout
- Not used on receiver (OLED always on)
@@ -43,6 +43,9 @@ Variants:
### Notes on GPIOs
- GPIO34/35/36/39 are input-only and have **no internal pullups/pulldowns**.
- Strap pins (GPIO0/2/4/5/12/15) can affect boot; avoid for role or control jumpers.
- GPIO14 is shared between role select and SD SCK. **Do not attach the role jumper in Receiver mode if the SD card is connected/used**, and never force GPIO14 high when using SD.
- GPIO13 is shared between OLED control and SD CS. Avoid driving OLED control when SD is active.
- Receiver firmware releases GPIO14 to `INPUT` (no pulldown) after boot before SD SPI init.
## Firmware Roles
### Sender (battery-powered)
@@ -262,10 +265,20 @@ inline constexpr uint16_t EXPECTED_SENDER_IDS[NUM_SENDERS] = { 0xF19C };
- `/wifi`: WiFi/MQTT/NTP config (AP and STA)
- `/sender/<device_id>`: per-sender details
- Sender IDs on `/` are clickable (open sender page in a new tab).
- In STA mode, the UI is also available via the boards IP/hostname on your WiFi network.
- In STA mode, the UI is also available via the board's IP/hostname on your WiFi network.
- Main page shows SD card file listing (downloadable).
- Sender page includes a history chart (power) with configurable range/resolution/mode.
## Security
- Basic Auth is supported for the web UI. In STA mode it is enabled by default; AP mode is optional.
- Config flags in `include/config.h`:
- `WEB_AUTH_REQUIRE_STA` (default `true`)
- `WEB_AUTH_REQUIRE_AP` (default `false`)
- `WEB_AUTH_DEFAULT_USER` / `WEB_AUTH_DEFAULT_PASS`
- Web credentials are stored in NVS. `/wifi`, `/sd/download`, `/history/*`, `/`, `/sender/*`, and `/manual` require auth when enabled.
- Password inputs are not prefilled. Leaving a password blank keeps the stored value; use the "clear password" checkbox to erase it.
- User-controlled strings are HTML-escaped before embedding in pages.
## MQTT
- Topic: `smartmeter/<deviceId>/state`
- QoS 0
@@ -303,6 +316,7 @@ Key timing settings in `include/config.h`:
- `ENABLE_SD_LOGGING` / `PIN_SD_CS`
- `SD_HISTORY_MAX_DAYS` / `SD_HISTORY_MIN_RES_MIN`
- `SD_HISTORY_MAX_BINS` / `SD_HISTORY_TIME_BUDGET_MS`
- `WEB_AUTH_REQUIRE_STA` / `WEB_AUTH_REQUIRE_AP` / `WEB_AUTH_DEFAULT_USER` / `WEB_AUTH_DEFAULT_PASS`
## Limits & Known Constraints
- **Compression**: MeterData uses lightweight RLE (good for JSON but not optimal).
@@ -320,6 +334,7 @@ Optional CSV logging to microSD (FAT32) when `ENABLE_SD_LOGGING = true`.
`ts_utc,p_w,p1_w,p2_w,p3_w,e_kwh,bat_v,bat_pct,rssi,snr,err_m,err_d,err_tx,err_last`
- `err_last` is written as text (`meter`, `decode`, `loratx`) only on the last sample of a batch that reports an error.
- Files are downloadable from the main UI page.
- Downloads only allow absolute paths under `/dd3/`, reject `..`, backslashes, and repeated slashes, and enforce a max path length.
- History chart on sender page stream-parses CSVs and bins data in the background.
- SD uses the on-board microSD SPI pins (CS=13, MOSI=15, SCK=14, MISO=2).
@@ -341,7 +356,7 @@ Optional CSV logging to microSD (FAT32) when `ENABLE_SD_LOGGING = true`.
- `src/main.cpp`: role detection and main loop
## Quick Start
1. Set role jumper on GPIO13:
1. Set role jumper on GPIO14:
- LOW: sender
- HIGH: receiver
2. OLED control on GPIO13: