From 4d16c51fc165ad7f471023e49ec4deec61942285 Mon Sep 17 00:00:00 2001 From: marty1885 Date: Wed, 11 Nov 2026 10:10:56 +0000 Subject: [PATCH 1/2] websocket validate Sec-WebSocket-Key to RFC standard and better upgrade path handling --- lib/src/HttpServer.cc | 18 ++++++++++++++++++ lib/src/Utilities.cc | 5 +++-- lib/src/WebSocketClientImpl.cc | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index 72431db4c1..cd91f4578f 100644 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -47,6 +47,7 @@ using namespace drogon; using namespace trantor; static inline bool isWebSocket(const HttpRequestImplPtr &req); +static bool isValidWebSocketKey(std::string_view key); static inline HttpResponsePtr tryDecompressRequest( const HttpRequestImplPtr &req); static inline bool passSyncAdvices( @@ -736,6 +737,14 @@ void HttpServer::websocketRequestRouting( std::function &&callback, WebSocketConnectionImplPtr &&wsConnPtr) { + if (!isValidWebSocketKey(req->getHeaderBy("sec-websocket-key"))) + { + auto resp = HttpResponse::newHttpResponse(k400BadRequest, CT_NONE); + resp->setCloseConnection(true); + callback(resp); + return; + } + RouteResult result = HttpControllersRouter::instance().routeWs(req); if (result.result == RouteResult::Success) @@ -1157,6 +1166,15 @@ void HttpServer::sendResponses( buffer.retrieveAll(); } +static bool isValidWebSocketKey(std::string_view key) +{ + if (key.size() != 24 || !utils::isBase64(key)) + return false; + + const auto decoded = utils::base64Decode(key); + return decoded.size() == 16 && utils::base64Encode(decoded) == key; +} + static inline bool isWebSocket(const HttpRequestImplPtr &req) { if (req->method() != Get) diff --git a/lib/src/Utilities.cc b/lib/src/Utilities.cc index f82472850e..59248c21f5 100644 --- a/lib/src/Utilities.cc +++ b/lib/src/Utilities.cc @@ -911,8 +911,9 @@ std::string urlDecode(const char *begin, const char *end) result += ' '; break; case '%': - if ((i + 2) < len && isxdigit(begin[i + 1]) && - isxdigit(begin[i + 2])) + if ((i + 2) < len && + isxdigit(static_cast(begin[i + 1])) && + isxdigit(static_cast(begin[i + 2]))) { unsigned int x1 = begin[i + 1]; if (x1 >= '0' && x1 <= '9') diff --git a/lib/src/WebSocketClientImpl.cc b/lib/src/WebSocketClientImpl.cc index f979668291..360f27df90 100644 --- a/lib/src/WebSocketClientImpl.cc +++ b/lib/src/WebSocketClientImpl.cc @@ -256,6 +256,8 @@ void WebSocketClientImpl::onRecvMessage( auto acceptStr = resp->getHeaderBy("sec-websocket-accept"); if (resp->statusCode() != k101SwitchingProtocols || + !utils::ci_equals(resp->getHeaderBy("upgrade"), "websocket") || + !containsHttpToken(resp->getHeaderBy("connection"), "upgrade") || acceptStr != wsAccept_) { requestCallback_(ReqResult::BadResponse, From 867c890d7e928b8aadd5ca4abe17533ae5bc0ada Mon Sep 17 00:00:00 2001 From: marty1885 Date: Wed, 11 Nov 2026 10:21:02 +0000 Subject: [PATCH 2/2] oops --- lib/src/HttpServer.cc | 6 +++--- lib/src/WebSocketClientImpl.cc | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index cd91f4578f..a9b1aa42db 100644 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -1168,11 +1168,11 @@ void HttpServer::sendResponses( static bool isValidWebSocketKey(std::string_view key) { - if (key.size() != 24 || !utils::isBase64(key)) + if (key.size() != 24 || !drogon::utils::isBase64(key)) return false; - const auto decoded = utils::base64Decode(key); - return decoded.size() == 16 && utils::base64Encode(decoded) == key; + const auto decoded = drogon::utils::base64Decode(key); + return decoded.size() == 16 && drogon::utils::base64Encode(decoded) == key; } static inline bool isWebSocket(const HttpRequestImplPtr &req) diff --git a/lib/src/WebSocketClientImpl.cc b/lib/src/WebSocketClientImpl.cc index 360f27df90..c76e39af59 100644 --- a/lib/src/WebSocketClientImpl.cc +++ b/lib/src/WebSocketClientImpl.cc @@ -256,7 +256,8 @@ void WebSocketClientImpl::onRecvMessage( auto acceptStr = resp->getHeaderBy("sec-websocket-accept"); if (resp->statusCode() != k101SwitchingProtocols || - !utils::ci_equals(resp->getHeaderBy("upgrade"), "websocket") || + !drogon::utils::ci_equals(resp->getHeaderBy("upgrade"), + "websocket") || !containsHttpToken(resp->getHeaderBy("connection"), "upgrade") || acceptStr != wsAccept_) {