From 83e7eefc2108136be92462a774840b6635bbef38 Mon Sep 17 00:00:00 2001 From: MarkLee131 Date: Mon, 4 May 2026 20:35:23 +0800 Subject: [PATCH] HTTPSock: tighten hardening header defaults Switch the default Referrer-Policy from same-origin to no-referrer so the webadmin URL (which can carry user/network names in the path) does not leak to outbound clicks either. Drop Pragma: no-cache; it is deprecated and modern intermediaries honor Cache-Control. Simplify Cache-Control to a single no-store directive, which on its own already prevents storing per RFC 9111; the previous no-cache, must-revalidate, max-age=0 tail was HTTP/1.0-era padding. --- src/HTTPSock.cpp | 6 ++---- test/HTTPSockTest.cpp | 16 ++++++---------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/HTTPSock.cpp b/src/HTTPSock.cpp index 672f67ea..9b27a7ce 100644 --- a/src/HTTPSock.cpp +++ b/src/HTTPSock.cpp @@ -793,7 +793,7 @@ void CHTTPSock::WriteHardeningHeaders(unsigned int uStatusId) { // entirely via OmitHardeningHeader, before PrintHeader runs. writeIfWanted("X-Frame-Options", "SAMEORIGIN"); writeIfWanted("X-Content-Type-Options", "nosniff"); - writeIfWanted("Referrer-Policy", "same-origin"); + writeIfWanted("Referrer-Policy", "no-referrer"); // Don't cache authenticated/dynamic responses. Skip for 304 and for // static asset MIME types that the ETag/Last-Modified path handles @@ -804,9 +804,7 @@ void CHTTPSock::WriteHardeningHeaders(unsigned int uStatusId) { m_sContentType.StartsWith("text/css") || m_sContentType.StartsWith("application/javascript"); if (!bStaticLike) { - writeIfWanted("Cache-Control", - "no-store, no-cache, must-revalidate, max-age=0"); - writeIfWanted("Pragma", "no-cache"); + writeIfWanted("Cache-Control", "no-store"); } } diff --git a/test/HTTPSockTest.cpp b/test/HTTPSockTest.cpp index ae41a1a6..0b043642 100644 --- a/test/HTTPSockTest.cpp +++ b/test/HTTPSockTest.cpp @@ -79,8 +79,8 @@ class HTTPSockHeadersTest : public ::testing::Test { // Hardening response headers introduced for #2012. The fix's contract: // - emit X-Frame-Options, X-Content-Type-Options, Referrer-Policy on every // response (unless the caller already set them or asked to omit them); -// - emit no-store Cache-Control + Pragma for dynamic responses, but skip -// for 304 and for static asset MIME types whose freshness is handled by +// - emit a no-store Cache-Control for dynamic responses, but skip it for +// 304 and for static asset MIME types whose freshness is handled by // ETag/Last-Modified; // - never duplicate a header the caller already added via AddHeader; // - skip a header entirely when the caller calls OmitHardeningHeader. @@ -89,9 +89,8 @@ TEST_F(HTTPSockHeadersTest, HardeningHeadersDefaultDynamicResponse) { sock.PrintHeader(0, "text/html"); EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("X-Frame-Options: SAMEORIGIN"))); EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("X-Content-Type-Options: nosniff"))); - EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("Referrer-Policy: same-origin"))); - EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("Cache-Control:"))); - EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("Pragma: no-cache"))); + EXPECT_THAT(sock.m_vsLines, Contains(CString("Referrer-Policy: no-referrer\r\n"))); + EXPECT_THAT(sock.m_vsLines, Contains(CString("Cache-Control: no-store\r\n"))); } TEST_F(HTTPSockHeadersTest, HardeningHeadersSkipCacheControlOn304) { @@ -99,7 +98,6 @@ TEST_F(HTTPSockHeadersTest, HardeningHeadersSkipCacheControlOn304) { sock.PrintHeader(0, "text/html", 304, "Not Modified"); EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("X-Frame-Options:"))); EXPECT_THAT(sock.m_vsLines, Not(Contains(StartsWith("Cache-Control:")))); - EXPECT_THAT(sock.m_vsLines, Not(Contains(StartsWith("Pragma:")))); } TEST_F(HTTPSockHeadersTest, HardeningHeadersSkipCacheControlForStaticAssets) { @@ -128,7 +126,7 @@ TEST_F(HTTPSockHeadersTest, HardeningHeadersDeferToCallerXFrameOptions) { EXPECT_THAT(sock.m_vsLines, Contains(CString("X-Frame-Options: DENY\r\n"))); // Other defaults still emitted. EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("X-Content-Type-Options: nosniff"))); - EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("Referrer-Policy: same-origin"))); + EXPECT_THAT(sock.m_vsLines, Contains(CString("Referrer-Policy: no-referrer\r\n"))); } TEST_F(HTTPSockHeadersTest, HardeningHeadersDeferToCallerCacheControl) { @@ -138,8 +136,6 @@ TEST_F(HTTPSockHeadersTest, HardeningHeadersDeferToCallerCacheControl) { // No no-store default; only the caller's value should be emitted. EXPECT_THAT(sock.m_vsLines, Not(Contains(StartsWith("Cache-Control: no-store")))); EXPECT_THAT(sock.m_vsLines, Contains(CString("Cache-Control: max-age=300\r\n"))); - // Pragma is independently controlled and still emitted as a default. - EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("Pragma: no-cache"))); } TEST_F(HTTPSockHeadersTest, HardeningHeadersOmittedByCaller) { @@ -152,5 +148,5 @@ TEST_F(HTTPSockHeadersTest, HardeningHeadersOmittedByCaller) { EXPECT_THAT(sock.m_vsLines, Not(Contains(StartsWith("Cache-Control:")))); // Other defaults unaffected. EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("X-Content-Type-Options: nosniff"))); - EXPECT_THAT(sock.m_vsLines, Contains(StartsWith("Pragma: no-cache"))); + EXPECT_THAT(sock.m_vsLines, Contains(CString("Referrer-Policy: no-referrer\r\n"))); }