diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index c4fc043f42..5971d396c4 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -309,8 +309,8 @@ void AsyncWebServerRequest::init_response_(AsyncWebServerResponse *rsp, int code } httpd_resp_set_hdr(*this, "Accept-Ranges", "none"); - for (const auto &pair : DefaultHeaders::Instance().headers_) { - httpd_resp_set_hdr(*this, pair.first.c_str(), pair.second.c_str()); + for (const auto &header : DefaultHeaders::Instance().headers_) { + httpd_resp_set_hdr(*this, header.name, header.value); } delete this->rsp_; @@ -335,17 +335,30 @@ bool AsyncWebServerRequest::authenticate(const char *username, const char *passw return false; } - std::string user_info; - user_info += username; - user_info += ':'; - user_info += password; + // Build user:pass in stack buffer (max 64 + 1 + 64 = 129 bytes typical) + // Use 256 bytes to handle longer credentials safely + constexpr size_t max_user_info_len = 256; + char user_info[max_user_info_len]; + size_t user_len = strlen(username); + size_t pass_len = strlen(password); + size_t user_info_len = user_len + 1 + pass_len; + + if (user_info_len >= max_user_info_len) { + ESP_LOGW(TAG, "Credentials too long for authentication"); + return false; + } + + memcpy(user_info, username, user_len); + user_info[user_len] = ':'; + memcpy(user_info + user_len + 1, password, pass_len); + user_info[user_info_len] = '\0'; size_t n = 0, out; - esp_crypto_base64_encode(nullptr, 0, &n, reinterpret_cast(user_info.c_str()), user_info.size()); + esp_crypto_base64_encode(nullptr, 0, &n, reinterpret_cast(user_info), user_info_len); auto digest = std::unique_ptr(new char[n + 1]); esp_crypto_base64_encode(reinterpret_cast(digest.get()), n, &out, - reinterpret_cast(user_info.c_str()), user_info.size()); + reinterpret_cast(user_info), user_info_len); return strcmp(digest.get(), auth_str + auth_prefix_len) == 0; } @@ -484,8 +497,8 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest * httpd_resp_set_hdr(req, "Cache-Control", "no-cache"); httpd_resp_set_hdr(req, "Connection", "keep-alive"); - for (const auto &pair : DefaultHeaders::Instance().headers_) { - httpd_resp_set_hdr(req, pair.first.c_str(), pair.second.c_str()); + for (const auto &header : DefaultHeaders::Instance().headers_) { + httpd_resp_set_hdr(req, header.name, header.value); } httpd_resp_send_chunk(req, CRLF_STR, CRLF_LEN); diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index 5f9f598388..bce2467ade 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -326,6 +326,11 @@ class AsyncEventSource : public AsyncWebHandler { }; #endif // USE_WEBSERVER +struct HttpHeader { + const char *name; + const char *value; +}; + class DefaultHeaders { friend class AsyncWebServerRequest; #ifdef USE_WEBSERVER @@ -334,13 +339,13 @@ class DefaultHeaders { public: // NOLINTNEXTLINE(readability-identifier-naming) - void addHeader(const char *name, const char *value) { this->headers_.emplace_back(name, value); } + void addHeader(const char *name, const char *value) { this->headers_.push_back({name, value}); } // NOLINTNEXTLINE(readability-identifier-naming) static DefaultHeaders &Instance(); protected: - std::vector> headers_; + std::vector headers_; }; } // namespace web_server_idf