Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Loader. Source: X post from @slonser_
DescriptionInsufficient policy enforcement in Loader. Source: X post from @slonser_
ComponentChromium
Bug ClassLogic Error
Tracker415810136
Fix commit1ea5b9417cd9 (chromium/src) +72/-7
CISA KEVNot listed
Disclosed2025-05-14

Changed Functions

FunctionChangeNotes
switch
third_party/blink/renderer/core/loader/preload_helper.cc
modified

Files Changed

  • third_party/blink/common/features.cc
  • third_party/blink/public/common/features.h
  • third_party/blink/renderer/core/loader/preload_helper.cc
From 1ea5b9417cd993d0ddb15fec186fed016198050c Mon Sep 17 00:00:00 2001
From: Takashi Nakayama <tnak@chromium.org>
Date: Tue, 09 Dec 2025 22:48:32 -0800
Subject: [PATCH] Add features to restrict Link headers on subresource responses

This CL implements a feature flag to partially disable HTTP Link headers
on subresource responses for experiments. The prepared options are based
on a proposal doc [1].

[1]
https://docs.google.com/document/d/1OeqpA9JoCXrgIMpq-ujLuZF1tG9MNF5SA0zTuIFtZis/edit?usp=sharing

Bug: 415810136
Change-Id: I473313c350d3acf1c2a5808c551152a47e69ac21
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7172079
Commit-Queue: Takashi Nakayama <tnak@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1556546}
---

diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 311a725..03ce59c 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -2084,6 +2084,29 @@
 BASE_FEATURE(kResourceFetcherStoresStrongReferences,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kRestrictLinkHeaderOnSubresource,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE_PARAM(bool,
+                   kRestrictLinkHeaderOnSubresourceCompressionDictionary,
+                   &kRestrictLinkHeaderOnSubresource,
+                   "disable_compression_dictionary",
+                   false);
+BASE_FEATURE_PARAM(bool,
+                   kRestrictLinkHeaderOnSubresourceCrossOrigin,
+                   &kRestrictLinkHeaderOnSubresource,
+                   "disable_cross_origin",
+                   false);
+BASE_FEATURE_PARAM(bool,
+                   kRestrictLinkHeaderOnSubresourceNetworkHint,
+                   &kRestrictLinkHeaderOnSubresource,
+                   "disable_network_hint",
+                   false);
+BASE_FEATURE_PARAM(bool,
+                   kRestrictLinkHeaderOnSubresourceResourceLoad,
+                   &kRestrictLinkHeaderOnSubresource,
+                   "disable_resource_load",
+                   false);
+
 BASE_FEATURE(kRestrictSpellingAndGrammarHighlights,
              base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE_PARAM(bool,
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index d3f5e38..07085cd 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -1616,6 +1616,27 @@
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
     kResourceFetcherStoresStrongReferences);
 
+// Aggregated flag for the restriction on HTTP Link headers on subresource
+// responses. See crbug.com/417529151 for details.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kRestrictLinkHeaderOnSubresource);
+// Disables only "rel=compression-dictionary".
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM(
+    bool,
+    kRestrictLinkHeaderOnSubresourceCompressionDictionary);
+// Disables all types of chained-preloads from cross-origin subresource
+// responses.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM(
+    bool,
+    kRestrictLinkHeaderOnSubresourceCrossOrigin);
+// Disables "rel=dns-prefetch" and "rel=preconnect".
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM(
+    bool,
+    kRestrictLinkHeaderOnSubresourceNetworkHint);
+// Disables "rel=preload", "rel=modulepreload", and "rel=prefetch".
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM(
+    bool,
+    kRestrictLinkHeaderOnSubresourceResourceLoad);
+
 // When enabled, it adds Payto URI Scheme to the safe list for
 // registerProtocolHandler. This feature is disabled by default
 // Payto URI Scheme explanation https://datatracker.ietf.org/doc/html/rfc8905
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index cfc2dabe..75637de8 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -191,7 +191,12 @@
          as == "video" || as == "worker" || as == "xslt";
 }
 
-bool IsNetworkHintAllowed(PreloadHelper::LoadLinksFromHeaderMode mode) {
+bool IsNetworkHintAllowed(PreloadHelper::LoadLinksFromHeaderMode mode,
+                          bool is_header_on_subresource) {
+  if (is_header_on_subresource &&
+      blink::features::kRestrictLinkHeaderOnSubresourceNetworkHint.Get()) {
+    return false;
+  }
   switch (mode) {
     case PreloadHelper::LoadLinksFromHeaderMode::kDocumentBeforeCommit:
       return true;
@@ -211,7 +216,12 @@
 }
 
 bool IsResourceLoadAllowed(PreloadHelper::LoadLinksFromHeaderMode mode,
-                           bool is_viewport_dependent) {
+                           bool is_viewport_dependent,
+                           bool is_header_on_subresource) {
+  if (is_header_on_subresource &&
+      blink::features::kRestrictLinkHeaderOnSubresourceResourceLoad.Get()) {
+    return false;
+  }
   switch (mode) {
     case PreloadHelper::LoadLinksFromHeaderMode::kDocumentBeforeCommit:
       return false;
@@ -231,7 +241,13 @@
 }
 
 bool IsCompressionDictionaryLoadAllowed(
-    PreloadHelper::LoadLinksFromHeaderMode mode) {
+    PreloadHelper::LoadLinksFromHeaderMode mode,
+    bool is_header_on_subresource) {
+  if (is_header_on_subresource &&
+      blink::features::kRestrictLinkHeaderOnSubresourceCompressionDictionary
+          .Get()) {
+    return false;
+  }
   // Document header can trigger dictionary load after the page load completes.
   // Subresources header can trigger dictionary load if it is not from the
   // memory cache.
@@ -828,11 +844,12 @@
     if (!header.Valid() || header.Url().empty() || header.Rel().empty()) {
       continue;
     }
-    bool is_network_hint_allowed = IsNetworkHintAllowed(mode);
-    bool is_resource_load_allowed =
-        IsResourceLoadAllowed(mode, header.IsViewportDependent());
+    bool is_network_hint_allowed =
+        IsNetworkHintAllowed(mode, is_subresource_load);
+    bool is_resource_load_allowed = IsResourceLoadAllowed(
+        mode, header.IsViewportDependent(), is_subresource_load);
     bool is_compression_dictionary_load_allowed =
-        IsCompressionDictionaryLoadAllowed(mode);
+        IsCompressionDictionaryLoadAllowed(mode, is_subresource_load);
     if (!is_network_hint_allowed && !is_resource_load_allowed &&
         !is_compression_dictionary_load_allowed) {
       // Skip this `header`; it won't initiate any types of preloading.
@@ -856,6 +873,10 @@
           .SetOriginStatusOnSubresource(base::to_underlying(origin_status))
           .Record(document->UkmRecorder());
     }
+    if (is_subresource_load && !from_same_origin &&
+        blink::features::kRestrictLinkHeaderOnSubresourceCrossOrigin.Get()) {
+      continue;
+    }
 
     // For security purposes, set `referrerpolicy: "no-referrer"` in link loads
     // from subresources. See https://crbug.com/415810136 for details.
Loading diff…

Original Bug Report

reported by se...@gmail.com

Cross-origin image loaded in <img> tag can make additional request and leak referrer

VULNERABILITY DETAILS

See this tweet. Chrome allows triggering additional network requests from subresources (e.g. images) by following Link header. AFAIK, resources loaded in <img> tag is inert, and therefore it can’t make additional network requests, animate (in case of SVG), etc.

This allows leaking referrer, which should be origin when cross-origin by default, which seems to be bypassed here.

CREDIT INFORMATION

Reporter credit: https://x.com/slonser_

View on issue tracker