From 939d2d60b4ba82c1d1a5fa018e22423db4a94d85 Mon Sep 17 00:00:00 2001 From: dengwenjun Date: Wed, 3 Sep 2025 16:05:35 +0800 Subject: [PATCH] Validate Request to prevent cross-domain attacks Validate Request to prevent cross-domain attacks Issue:https://gitee.com/openharmony/arkcompiler_toolchain/issues/ICW5UH Signed-off-by: dengwenjun --- inspector/inspector.cpp | 2 +- inspector/library_loader.cpp | 2 +- websocket/http.cpp | 30 ++++++-- websocket/http.h | 7 +- websocket/test/http_decoder_test.cpp | 102 +++++++++++++++++++++++++-- 5 files changed, 129 insertions(+), 14 deletions(-) diff --git a/inspector/inspector.cpp b/inspector/inspector.cpp index 2d843c75..3c49ff99 100644 --- a/inspector/inspector.cpp +++ b/inspector/inspector.cpp @@ -509,7 +509,7 @@ void StopDebug(void* vm, bool isHybrid) uint32_t tid = g_inspectors[vm]->tid_; #endif #if defined(OHOS_PLATFORM) - tid = g_inspectors[vm]->tidForSocketPair_; + tid = static_cast(g_inspectors[vm]->tidForSocketPair_); #endif // defined(OHOS_PLATFORM) auto debuggerInfo = g_debuggerInfo.find(tid); if (debuggerInfo != g_debuggerInfo.end()) { diff --git a/inspector/library_loader.cpp b/inspector/library_loader.cpp index e13b8bb6..48b900d3 100644 --- a/inspector/library_loader.cpp +++ b/inspector/library_loader.cpp @@ -70,7 +70,7 @@ void* Load(std::string_view libraryName) if (handle != nullptr) { return handle; } - LOGE("Failed to open %{public}s, reason:%{public}sn", libraryName.data(), dlerror()); + LOGE("Failed to open %{public}s, reason:%{public}s", libraryName.data(), dlerror()); return nullptr; } diff --git a/websocket/http.cpp b/websocket/http.cpp index 888e40b9..9e887de1 100644 --- a/websocket/http.cpp +++ b/websocket/http.cpp @@ -17,6 +17,28 @@ #include "http.h" namespace OHOS::ArkCompiler::Toolchain { + +const std::regex HttpRequest::localUrlRegex( + R"(^(https?:\/\/)(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$)", std::regex_constants::icase +); +const std::regex HttpRequest::httpPrefixRegex(R"(^(https?:\/\/).*)"); + +bool HttpRequest::ValidateHttpRequestOrigin(const std::string& httpRequestOrigin) +{ + // An empty Origin header is considered as safe + if (httpRequestOrigin.empty()) { + return true; + } + + // If the Origin header does not start with "http://" or "https://", it is considered as safe + if (!std::regex_match(httpRequestOrigin, httpPrefixRegex)) { + return true; + } + + // If it start with "http://" or "https://", it must be followed by the local IP to be considered secure + return std::regex_match(httpRequestOrigin, localUrlRegex); +} + /* static */ std::string HttpBase::DecodeHeader(const std::string& headersText, std::string_view headerName) { @@ -62,16 +84,14 @@ bool HttpRequest::Decode(const std::string& request, HttpRequest& parsed) return false; } - if (request.find(ORIGIN) != std::string::npos) { - return false; - } - parsed.version = DecodeVersion(request, pos); parsed.connection = DecodeHeader(request, CONNECTION); parsed.upgrade = DecodeHeader(request, UPGRADE); parsed.secWebSocketKey = DecodeHeader(request, SEC_WEBSOCKET_KEY); + parsed.origin = DecodeHeader(request, ORIGIN); - return true; + // Validate Origin field to prevent cross-domain attacks + return ValidateHttpRequestOrigin(parsed.origin); } /* static */ diff --git a/websocket/http.h b/websocket/http.h index 04efd2d4..da450061 100644 --- a/websocket/http.h +++ b/websocket/http.h @@ -17,6 +17,7 @@ #define ARKCOMPILER_TOOLCHAIN_WEBSOCKET_HTTP_H #include +#include namespace OHOS::ArkCompiler::Toolchain { struct HttpBase { @@ -37,9 +38,13 @@ struct HttpRequest : private HttpBase { std::string connection; std::string upgrade; std::string secWebSocketKey; + std::string origin; - static bool Decode(const std::string& request, HttpRequest& parsed); + static const std::regex localUrlRegex; + static const std::regex httpPrefixRegex; + static bool Decode(const std::string& request, HttpRequest& parsed); + static bool ValidateHttpRequestOrigin(const std::string& httpRequestOrigin); private: static std::string DecodeVersion(const std::string& request, std::string::size_type methodStartPos); }; diff --git a/websocket/test/http_decoder_test.cpp b/websocket/test/http_decoder_test.cpp index ac111ee6..6155bef2 100644 --- a/websocket/test/http_decoder_test.cpp +++ b/websocket/test/http_decoder_test.cpp @@ -23,7 +23,7 @@ using namespace OHOS::ArkCompiler::Toolchain; namespace panda::test { class HttpDecoderTest : public testing::Test { public: - static constexpr std::string_view REQUEST_ORIGIN_HEADERS = "GET / HTTP/1.1\r\n" + static constexpr std::string_view REQUEST_HEADERS_ONE = "GET / HTTP/1.1\r\n" "Host: 127.0.0.1:19015\r\n" "Connection: Upgrade\r\n" "Pragma: no-cache\r\n" @@ -36,9 +36,9 @@ public: "Accept-Language: en-US,en;q=0.9\r\n" "Sec-WebSocket-Key: AyuTxzyBTJJdViDskomT0Q==\r\n" "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n"; - std::string originRequestHeaders = std::string(REQUEST_ORIGIN_HEADERS); + std::string requestHeadersOne = std::string(REQUEST_HEADERS_ONE); - static constexpr std::string_view REQUEST_HEADERS = "GET / HTTP/1.1\r\n" + static constexpr std::string_view REQUEST_HEADERS_TWO = "GET / HTTP/1.1\r\n" "Host: 127.0.0.1:19015\r\n" "Connection: Upgrade\r\n" "Pragma: no-cache\r\n" @@ -50,7 +50,52 @@ public: "Accept-Language: en-US,en;q=0.9\r\n" "Sec-WebSocket-Key: AyuTxzyBTJJdViDskomT0Q==\r\n" "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n"; - std::string requestHeaders = std::string(REQUEST_HEADERS); + std::string requestHeadersTwo = std::string(REQUEST_HEADERS_TWO); + + static constexpr std::string_view REQUEST_HEADERS_THREE = "GET / HTTP/1.1\r\n" + "Host: 127.0.0.1:19015\r\n" + "Connection: Upgrade\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) Chrome/117.0.0.0 Safari/537.36\r\n" + "Upgrade: websocket\r\n" + "Origin: http://192.168.43.4:8000\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Accept-Language: en-US,en;q=0.9\r\n" + "Sec-WebSocket-Key: AyuTxzyBTJJdViDskomT0Q==\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n"; + std::string requestHeadersThree = std::string(REQUEST_HEADERS_THREE); + + static constexpr std::string_view REQUEST_HEADERS_FOUR = "GET / HTTP/1.1\r\n" + "Host: 127.0.0.1:19015\r\n" + "Connection: Upgrade\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) Chrome/117.0.0.0 Safari/537.36\r\n" + "Upgrade: websocket\r\n" + "Origin: https://127.0.0.1:8000\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Accept-Language: en-US,en;q=0.9\r\n" + "Sec-WebSocket-Key: AyuTxzyBTJJdViDskomT0Q==\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n"; + std::string requestHeadersFour = std::string(REQUEST_HEADERS_FOUR); + + static constexpr std::string_view REQUEST_HEADERS_FIVE = "GET / HTTP/1.1\r\n" + "Host: 127.0.0.1:19015\r\n" + "Connection: Upgrade\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) Chrome/117.0.0.0 Safari/537.36\r\n" + "Upgrade: websocket\r\n" + "Origin: https://localhost\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Accept-Language: en-US,en;q=0.9\r\n" + "Sec-WebSocket-Key: AyuTxzyBTJJdViDskomT0Q==\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n"; + std::string requestHeadersFive = std::string(REQUEST_HEADERS_FIVE); static constexpr std::string_view ERR_REQUEST_HEADERS = "GEY\r\n"; std::string errRequestHeaders = std::string(ERR_REQUEST_HEADERS); @@ -80,21 +125,66 @@ public: HWTEST_F(HttpDecoderTest, TestRequestDecode_1, testing::ext::TestSize.Level0) { HttpRequest parsed; - auto succeeded = HttpRequest::Decode(requestHeaders, parsed); + auto succeeded = HttpRequest::Decode(requestHeadersOne, parsed); ASSERT_TRUE(succeeded); ASSERT_EQ(parsed.version, EXPECTED_VERSION); ASSERT_EQ(parsed.connection, EXPECTED_CONNECTION); ASSERT_EQ(parsed.upgrade, EXPECTED_UPGRADE); ASSERT_EQ(parsed.secWebSocketKey, EXPECTED_SEC_WEBSOCKET_KEY); + ASSERT_EQ(parsed.origin, "dvtls://dvtls"); } HWTEST_F(HttpDecoderTest, TestRequestDecode_2, testing::ext::TestSize.Level0) { HttpRequest parsed; - auto succeeded = HttpRequest::Decode(originRequestHeaders, parsed); + auto succeeded = HttpRequest::Decode(requestHeadersTwo, parsed); + + ASSERT_TRUE(succeeded); + ASSERT_EQ(parsed.version, EXPECTED_VERSION); + ASSERT_EQ(parsed.connection, EXPECTED_CONNECTION); + ASSERT_EQ(parsed.upgrade, EXPECTED_UPGRADE); + ASSERT_EQ(parsed.secWebSocketKey, EXPECTED_SEC_WEBSOCKET_KEY); + ASSERT_EQ(parsed.origin, ""); +} + +HWTEST_F(HttpDecoderTest, TestRequestDecode_3, testing::ext::TestSize.Level0) +{ + HttpRequest parsed; + auto succeeded = HttpRequest::Decode(requestHeadersThree, parsed); ASSERT_FALSE(succeeded); + ASSERT_EQ(parsed.version, EXPECTED_VERSION); + ASSERT_EQ(parsed.connection, EXPECTED_CONNECTION); + ASSERT_EQ(parsed.upgrade, EXPECTED_UPGRADE); + ASSERT_EQ(parsed.secWebSocketKey, EXPECTED_SEC_WEBSOCKET_KEY); + ASSERT_EQ(parsed.origin, "http://192.168.43.4:8000"); +} + +HWTEST_F(HttpDecoderTest, TestRequestDecode_4, testing::ext::TestSize.Level0) +{ + HttpRequest parsed; + auto succeeded = HttpRequest::Decode(requestHeadersFour, parsed); + + ASSERT_TRUE(succeeded); + ASSERT_EQ(parsed.version, EXPECTED_VERSION); + ASSERT_EQ(parsed.connection, EXPECTED_CONNECTION); + ASSERT_EQ(parsed.upgrade, EXPECTED_UPGRADE); + ASSERT_EQ(parsed.secWebSocketKey, EXPECTED_SEC_WEBSOCKET_KEY); + ASSERT_EQ(parsed.origin, "https://127.0.0.1:8000"); +} + +HWTEST_F(HttpDecoderTest, TestRequestDecode_5, testing::ext::TestSize.Level0) +{ + HttpRequest parsed; + auto succeeded = HttpRequest::Decode(requestHeadersFive, parsed); + + ASSERT_TRUE(succeeded); + ASSERT_EQ(parsed.version, EXPECTED_VERSION); + ASSERT_EQ(parsed.connection, EXPECTED_CONNECTION); + ASSERT_EQ(parsed.upgrade, EXPECTED_UPGRADE); + ASSERT_EQ(parsed.secWebSocketKey, EXPECTED_SEC_WEBSOCKET_KEY); + ASSERT_EQ(parsed.origin, "https://localhost"); } HWTEST_F(HttpDecoderTest, TestAbnormalRequestDecode, testing::ext::TestSize.Level0) -- Gitee