mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	[web_server_idf] Improve parameter caching security and reduce memory overhead
This commit is contained in:
		| @@ -190,8 +190,8 @@ esp_err_t AsyncWebServer::request_handler_(AsyncWebServerRequest *request) const | ||||
|  | ||||
| AsyncWebServerRequest::~AsyncWebServerRequest() { | ||||
|   delete this->rsp_; | ||||
|   for (const auto &pair : this->params_) { | ||||
|     delete pair.second;  // NOLINT(cppcoreguidelines-owning-memory) | ||||
|   for (auto *param : this->params_) { | ||||
|     delete param;  // NOLINT(cppcoreguidelines-owning-memory) | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -231,10 +231,23 @@ void AsyncWebServerRequest::redirect(const std::string &url) { | ||||
| } | ||||
|  | ||||
| void AsyncWebServerRequest::init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type) { | ||||
|   httpd_resp_set_status(*this, code == 200   ? HTTPD_200 | ||||
|                                : code == 404 ? HTTPD_404 | ||||
|                                : code == 409 ? HTTPD_409 | ||||
|                                              : to_string(code).c_str()); | ||||
|   // Set status code - use constants for common codes to avoid string allocation | ||||
|   const char *status; | ||||
|   switch (code) { | ||||
|     case 200: | ||||
|       status = HTTPD_200; | ||||
|       break; | ||||
|     case 404: | ||||
|       status = HTTPD_404; | ||||
|       break; | ||||
|     case 409: | ||||
|       status = HTTPD_409; | ||||
|       break; | ||||
|     default: | ||||
|       status = to_string(code).c_str(); | ||||
|       break; | ||||
|   } | ||||
|   httpd_resp_set_status(*this, status); | ||||
|  | ||||
|   if (content_type && *content_type) { | ||||
|     httpd_resp_set_type(*this, content_type); | ||||
| @@ -291,11 +304,14 @@ void AsyncWebServerRequest::requestAuthentication(const char *realm) const { | ||||
| #endif | ||||
|  | ||||
| AsyncWebParameter *AsyncWebServerRequest::getParam(const std::string &name) { | ||||
|   auto find = this->params_.find(name); | ||||
|   if (find != this->params_.end()) { | ||||
|     return find->second; | ||||
|   // Check cache first - only successful lookups are cached | ||||
|   for (auto *param : this->params_) { | ||||
|     if (param->name() == name) { | ||||
|       return param; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Look up value from query strings | ||||
|   optional<std::string> val = query_key_value(this->post_query_, name); | ||||
|   if (!val.has_value()) { | ||||
|     auto url_query = request_get_url_query(*this); | ||||
| @@ -304,11 +320,14 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const std::string &name) { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   AsyncWebParameter *param = nullptr; | ||||
|   if (val.has_value()) { | ||||
|     param = new AsyncWebParameter(val.value());  // NOLINT(cppcoreguidelines-owning-memory) | ||||
|   // Don't cache misses to prevent memory exhaustion from malicious requests | ||||
|   // with thousands of non-existent parameter lookups | ||||
|   if (!val.has_value()) { | ||||
|     return nullptr; | ||||
|   } | ||||
|   this->params_.insert({name, param}); | ||||
|  | ||||
|   auto *param = new AsyncWebParameter(name, val.value());  // NOLINT(cppcoreguidelines-owning-memory) | ||||
|   this->params_.push_back(param); | ||||
|   return param; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -24,10 +24,12 @@ namespace web_server_idf { | ||||
|  | ||||
| class AsyncWebParameter { | ||||
|  public: | ||||
|   AsyncWebParameter(std::string value) : value_(std::move(value)) {} | ||||
|   AsyncWebParameter(std::string name, std::string value) : name_(std::move(name)), value_(std::move(value)) {} | ||||
|   const std::string &name() const { return this->name_; } | ||||
|   const std::string &value() const { return this->value_; } | ||||
|  | ||||
|  protected: | ||||
|   std::string name_; | ||||
|   std::string value_; | ||||
| }; | ||||
|  | ||||
| @@ -168,7 +170,10 @@ class AsyncWebServerRequest { | ||||
|  protected: | ||||
|   httpd_req_t *req_; | ||||
|   AsyncWebServerResponse *rsp_{}; | ||||
|   std::map<std::string, AsyncWebParameter *> params_; | ||||
|   // Use vector instead of map/unordered_map: most requests have 0-3 params, so linear search | ||||
|   // is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid | ||||
|   // duplicate storage. Only successful lookups are cached to prevent memory exhaustion attacks. | ||||
|   std::vector<AsyncWebParameter *> params_; | ||||
|   std::string post_query_; | ||||
|   AsyncWebServerRequest(httpd_req_t *req) : req_(req) {} | ||||
|   AsyncWebServerRequest(httpd_req_t *req, std::string post_query) : req_(req), post_query_(std::move(post_query)) {} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user