fix: stop sending stored passwords and PIN back to clients

The settings routes returned the WiFi passphrase, MQTT password and the
login password and PIN in plain text, and the debug log printed them and
the API key. Responses now carry hasPassphrase/hasPassword/hasPin flags,
logs mask the values, and an omitted secret keeps the stored one.

The UI loads secret fields empty and treats a blank field as "keep" while
the SSID or username is unchanged. Clearing a secret without changing the
SSID or username is listed in docs/KNOWN_ISSUES.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-18 14:34:18 +02:00
parent e963a5adb3
commit 1fb4c4cfcd
8 changed files with 189 additions and 29 deletions

View file

@ -5,6 +5,7 @@
#include <Preferences.h>
#include "ConfigSettings.h"
#include "Utils.h"
#include "Secrets.h"
#include "esp_chip_info.h"
#include "esp_log.h"
@ -301,7 +302,7 @@ bool MQTTSettings::toJSON(JsonObject &obj) {
obj["hostname"] = this->hostname;
obj["port"] = this->port;
obj["username"] = this->username;
obj["password"] = this->password;
obj["hasPassword"] = hasSecret(this->password);
obj["rootTopic"] = this->rootTopic;
obj["discoTopic"] = this->discoTopic;
return true;
@ -312,7 +313,7 @@ bool MQTTSettings::fromJSON(JsonObject &obj) {
this->parseValueString(obj, "protocol", this->protocol, sizeof(this->protocol));
this->parseValueString(obj, "hostname", this->hostname, sizeof(this->hostname));
this->parseValueString(obj, "username", this->username, sizeof(this->username));
this->parseValueString(obj, "password", this->password, sizeof(this->password));
applySecret(obj["password"].as<const char *>(), this->password, sizeof(this->password));
this->parseValueString(obj, "rootTopic", this->rootTopic, sizeof(this->rootTopic));
this->parseValueString(obj, "discoTopic", this->discoTopic, sizeof(this->discoTopic));
if(!obj["port"].isNull()) this->port = obj["port"];
@ -478,16 +479,16 @@ bool SecuritySettings::begin() {
bool SecuritySettings::fromJSON(JsonObject &obj) {
if(!obj["type"].isNull()) this->type = static_cast<security_types>(obj["type"].as<uint8_t>());
this->parseValueString(obj, "username", this->username, sizeof(this->username));
this->parseValueString(obj, "password", this->password, sizeof(this->password));
this->parseValueString(obj, "pin", this->pin, sizeof(this->pin));
applySecret(obj["password"].as<const char *>(), this->password, sizeof(this->password));
applySecret(obj["pin"].as<const char *>(), this->pin, sizeof(this->pin));
if(!obj["permissions"].isNull()) this->permissions = obj["permissions"];
return true;
}
bool SecuritySettings::toJSON(JsonObject &obj) {
obj["type"] = static_cast<uint8_t>(this->type);
obj["username"] = this->username;
obj["password"] = this->password;
obj["pin"] = this->pin;
obj["hasPassword"] = hasSecret(this->password);
obj["hasPin"] = hasSecret(this->pin);
obj["permissions"] = this->permissions;
return true;
}
@ -516,7 +517,7 @@ bool SecuritySettings::load() {
}
void SecuritySettings::print() {
ESP_LOGD(TAG, "SECURITY Type:%u Username:[%s] Password:[%s] Pin:[%s] Permissions:%u",
static_cast<uint8_t>(this->type), this->username, this->password, this->pin, this->permissions);
static_cast<uint8_t>(this->type), this->username, maskSecret(this->password), maskSecret(this->pin), this->permissions);
}
WifiSettings::WifiSettings() {}
@ -526,14 +527,14 @@ bool WifiSettings::begin() {
}
bool WifiSettings::fromJSON(JsonObject &obj) {
this->parseValueString(obj, "ssid", this->ssid, sizeof(this->ssid));
this->parseValueString(obj, "passphrase", this->passphrase, sizeof(this->passphrase));
applySecret(obj["passphrase"].as<const char *>(), this->passphrase, sizeof(this->passphrase));
if(!obj["roaming"].isNull()) this->roaming = obj["roaming"];
if(!obj["hidden"].isNull()) this->hidden = obj["hidden"];
return true;
}
bool WifiSettings::toJSON(JsonObject &obj) {
obj["ssid"] = this->ssid;
obj["passphrase"] = this->passphrase;
obj["hasPassphrase"] = hasSecret(this->passphrase);
obj["roaming"] = this->roaming;
obj["hidden"] = this->hidden;
return true;
@ -579,7 +580,7 @@ String WifiSettings::mapEncryptionType(int type) {
return "Unknown";
}
void WifiSettings::print() {
ESP_LOGD(TAG, "WIFI Settings SSID: [%s] PassPhrase: [%s]", this->ssid, this->passphrase);
ESP_LOGD(TAG, "WIFI Settings SSID: [%s] PassPhrase: [%s]", this->ssid, maskSecret(this->passphrase));
}
void WifiSettings::printNetworks() {
int n = WiFi.scanNetworks(false, false);