Browse Source

chore: cherry-pick 67c9cbc784d6 from chromium (#36220)

Pedro Pontes 2 years ago
parent
commit
30046ca08d
2 changed files with 623 additions and 0 deletions
  1. 1 0
      patches/chromium/.patches
  2. 622 0
      patches/chromium/cherry-pick-67c9cbc784d6.patch

+ 1 - 0
patches/chromium/.patches

@@ -134,3 +134,4 @@ cherry-pick-65f0ef609c00.patch
 cherry-pick-cb9dff93f3d4.patch
 build_fix_building_with_enable_plugins_false.patch
 build_allow_electron_to_use_exec_script.patch
+cherry-pick-67c9cbc784d6.patch

+ 622 - 0
patches/chromium/cherry-pick-67c9cbc784d6.patch

@@ -0,0 +1,622 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Lukasz Anforowicz <[email protected]>
+Date: Tue, 30 Aug 2022 19:18:15 +0000
+Subject: Validate `source_context` in ExtensionHostMsg_OpenChannelToNativeApp.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+After this CL, the Browser process will verify `source_context` in the
+IPC payload of the ExtensionHostMsg_OpenChannelToNativeApp message and
+avoid processing malformed or spoofed IPCs.
+
+Change-Id: I9466dc076c4d07dbb4bec38973000dc0418565f6
+Bug: 1356234
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3854987
+Commit-Queue: Łukasz Anforowicz <[email protected]>
+Reviewed-by: Devlin Cronin <[email protected]>
+Cr-Commit-Position: refs/heads/main@{#1041118}
+
+diff --git a/chrome/browser/extensions/extension_security_exploit_browsertest.cc b/chrome/browser/extensions/extension_security_exploit_browsertest.cc
+index 19c9c52fa5a796c93e80f77029727d64acfdfa07..4e5215739f8533d25831f3302515861df179525f 100644
+--- a/chrome/browser/extensions/extension_security_exploit_browsertest.cc
++++ b/chrome/browser/extensions/extension_security_exploit_browsertest.cc
+@@ -10,6 +10,7 @@
+ #include "base/memory/scoped_refptr.h"
+ #include "base/memory/weak_ptr.h"
+ #include "base/test/bind.h"
++#include "build/build_config.h"
+ #include "chrome/browser/chrome_content_browser_client.h"
+ #include "chrome/browser/extensions/extension_browsertest.h"
+ #include "chrome/browser/extensions/extension_tab_util.h"
+@@ -40,6 +41,10 @@
+ #include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom-forward.h"
+ #include "url/gurl.h"
+ 
++#if !(BUILDFLAG(IS_FUCHSIA))
++#include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
++#endif
++
+ namespace extensions {
+ 
+ // Waits for a kill of the given RenderProcessHost and returns the
+@@ -233,6 +238,10 @@ class OpenChannelToExtensionExploitTest : public ExtensionBrowserTest {
+     return ipc_message_waiter_->WaitForMessage();
+   }
+ 
++  content::WebContents* active_web_contents() {
++    return browser()->tab_strip_model()->GetActiveWebContents();
++  }
++
+   // Asks the `extension_id` to inject `content_script` into `web_contents`.
+   // Returns true if the content script execution started successfully.
+   bool ExecuteProgrammaticContentScript(content::WebContents* web_contents,
+@@ -246,63 +255,86 @@ class OpenChannelToExtensionExploitTest : public ExtensionBrowserTest {
+         browser()->profile(), extension_id, background_script);
+   }
+ 
++  const Extension& active_extension() { return *active_extension_; }
+   const ExtensionId& active_extension_id() { return active_extension_->id(); }
+ 
+   const ExtensionId& spoofed_extension_id() { return spoofed_extension_->id(); }
+ 
+  private:
++  // Installs an `active_extension` and a separate, but otherwise identical
++  // `spoofed_extension` (the only difference will be the extension id).
+   void InstallTestExtensions() {
+-    // Install an `active_extension` and a separate, but otherwise identical
+-    // `spoofed_extension` (the only difference will be the extension id).
+-    auto install_extension = [this](TestExtensionDir& dir) -> const Extension* {
++    auto install_extension =
++        [this](TestExtensionDir& dir,
++               const char* extra_manifest_bits) -> const Extension* {
+       const char kManifestTemplate[] = R"(
+           {
++            %s
+             "name": "ContentScriptTrackerBrowserTest - Programmatic",
+             "version": "1.0",
+             "manifest_version": 2,
+-            "permissions": [ "tabs", "<all_urls>" ],
++            "permissions": [
++                "tabs",
++                "<all_urls>",
++                "nativeMessaging"
++            ],
+             "background": {"scripts": ["background_script.js"]}
+           } )";
+-      dir.WriteManifest(kManifestTemplate);
++      dir.WriteManifest(
++          base::StringPrintf(kManifestTemplate, extra_manifest_bits));
+       dir.WriteFile(FILE_PATH_LITERAL("background_script.js"), "");
++      dir.WriteFile(FILE_PATH_LITERAL("page.html"), "<p>page</p>");
+       return LoadExtension(dir.UnpackedPath());
+     };
+-    TestExtensionDir active_dir;
+-    TestExtensionDir spoofed_dir;
+-    active_extension_ = install_extension(active_dir);
+-    spoofed_extension_ = install_extension(spoofed_dir);
++#if !(BUILDFLAG(IS_FUCHSIA))
++    // The key below corresponds to the extension ID used by
++    // ScopedTestNativeMessagingHost::kExtensionId.
++    const char kActiveExtensionKey[] = R"(
++        "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
++    )";
++#else
++    // Native messaging is not available on Fuchsia (i.e.
++    // //chrome/browser/extensions/BUILD.gn excludes
++    // api/messaging/native_messaging_test_util.h on Fuchsia).
++    const char kActiveExtensionKey[] = "";
++#endif
++    active_extension_ = install_extension(active_dir_, kActiveExtensionKey);
++    spoofed_extension_ = install_extension(spoofed_dir_, "");
+     ASSERT_TRUE(active_extension_);
+     ASSERT_TRUE(spoofed_extension_);
++#if !(BUILDFLAG(IS_FUCHSIA))
++    ASSERT_EQ(active_extension_id(),
++              ScopedTestNativeMessagingHost::kExtensionId);
++#endif
+     ASSERT_NE(active_extension_id(), spoofed_extension_id());
+   }
+ 
+   using OpenChannelMessageWaiter =
+       ExtensionMessageWaiter<ExtensionHostMsg_OpenChannelToExtension>;
+-  std::unique_ptr<OpenChannelMessageWaiter> StartInterceptingIpcs(
+-      const GURL& test_page_url) {
+     // Start capturing IPC messages in all future/new RenderProcessHosts.
+-    auto ipc_message_waiter = std::make_unique<OpenChannelMessageWaiter>();
++    ipc_message_waiter_ = std::make_unique<OpenChannelMessageWaiter>();
+ 
+     // Navigate to an arbitrary, mostly empty test page.  Make sure that a new
+     // RenderProcessHost is created to make sure it is covered by the
+-    // `ipc_message_waiter`.  (A WebUI -> http navigation should swap the
++    // `ipc_message_waiter_`.  (A WebUI -> http navigation should swap the
+     // RenderProcessHost on all platforms.)
+-    content::WebContents* web_contents =
+-        browser()->tab_strip_model()->GetActiveWebContents();
++    GURL test_page_url =
++        embedded_test_server()->GetURL("foo.com", "/title1.html");
+     int old_process_id =
+-        web_contents->GetPrimaryMainFrame()->GetProcess()->GetID();
++        active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
+     EXPECT_TRUE(
+         ui_test_utils::NavigateToURL(browser(), GURL("chrome://version")));
+     EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), test_page_url));
+     int new_process_id =
+-        web_contents->GetPrimaryMainFrame()->GetProcess()->GetID();
++        active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
+     EXPECT_NE(old_process_id, new_process_id);
+ 
+     // Only intercept messages from `active_extension`'s content script running
+     // in the main frame's process.
+     std::string matching_extension_id = active_extension_id();
+-    int matching_process_id = new_process_id;
+-    ipc_message_waiter->SetIpcMatcher(base::BindLambdaForTesting(
++    int matching_process_id =
++        active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
++    ipc_message_waiter_->SetIpcMatcher(base::BindLambdaForTesting(
+         [matching_extension_id, matching_process_id](
+             int captured_render_process_id,
+             const ExtensionHostMsg_OpenChannelToExtension::Param& param) {
+@@ -319,12 +351,19 @@ class OpenChannelToExtensionExploitTest : public ExtensionBrowserTest {
+ 
+           return true;
+         }));
++  }
+ 
+-    return ipc_message_waiter;
++  // Waits for ExtensionHostMsg_OpenChannelToExtension IPC and returns its
++  // payload.
++  ExtensionHostMsg_OpenChannelToExtension::Param WaitForMessage() {
++    return ipc_message_waiter_->WaitForMessage();
+   }
+ 
++ private:
+   std::unique_ptr<OpenChannelMessageWaiter> ipc_message_waiter_;
+ 
++  TestExtensionDir active_dir_;
++  TestExtensionDir spoofed_dir_;
+   raw_ptr<const Extension> active_extension_ = nullptr;
+   raw_ptr<const Extension> spoofed_extension_ = nullptr;
+ };
+@@ -340,24 +379,22 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
+ 
+   // Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
+   // from a content script of an `active_extension_id`.
+-  content::WebContents* web_contents =
+-      browser()->tab_strip_model()->GetActiveWebContents();
+   ASSERT_TRUE(ExecuteProgrammaticContentScript(
+-      web_contents, active_extension_id(),
++      active_web_contents(), active_extension_id(),
+       "chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
+ 
+   // Capture the IPC.
+   auto [source_context, info, channel_name, port_id] = WaitForMessage();
+-
+-  // Mutate the IPC payload.
+   EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
+   EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
++
++  // Mutate the IPC payload.
+   info.source_endpoint.extension_id = spoofed_extension_id();
+ 
+   // Inject the malformed/mutated IPC and verify that the renderer is terminated
+   // as expected.
+   content::RenderProcessHost* main_frame_process =
+-      web_contents->GetPrimaryMainFrame()->GetProcess();
++      active_web_contents()->GetPrimaryMainFrame()->GetProcess();
+   RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
+   IPC::IpcSecurityTestUtil::PwnMessageReceived(
+       main_frame_process->GetChannel(),
+@@ -371,24 +408,22 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
+                        FromContentScript_UnexpectedNativeAppType) {
+   // Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
+   // from a content script of an `active_extension_id`.
+-  content::WebContents* web_contents =
+-      browser()->tab_strip_model()->GetActiveWebContents();
+   ASSERT_TRUE(ExecuteProgrammaticContentScript(
+-      web_contents, active_extension_id(),
++      active_web_contents(), active_extension_id(),
+       "chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
+ 
+   // Capture the IPC.
+   auto [source_context, info, channel_name, port_id] = WaitForMessage();
+-
+-  // Mutate the IPC payload.
+   EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
+   EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
++
++  // Mutate the IPC payload.
+   info.source_endpoint.type = MessagingEndpoint::Type::kNativeApp;
+ 
+   // Inject the malformed/mutated IPC and verify that the renderer is terminated
+   // as expected.
+   content::RenderProcessHost* main_frame_process =
+-      web_contents->GetPrimaryMainFrame()->GetProcess();
++      active_web_contents()->GetPrimaryMainFrame()->GetProcess();
+   RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
+   IPC::IpcSecurityTestUtil::PwnMessageReceived(
+       main_frame_process->GetChannel(),
+@@ -401,24 +436,22 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
+                        FromContentScript_UnexpectedExtensionType) {
+   // Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
+   // from a content script of an `active_extension_id`.
+-  content::WebContents* web_contents =
+-      browser()->tab_strip_model()->GetActiveWebContents();
+   ASSERT_TRUE(ExecuteProgrammaticContentScript(
+-      web_contents, active_extension_id(),
++      active_web_contents(), active_extension_id(),
+       "chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
+ 
+   // Capture the IPC.
+   auto [source_context, info, channel_name, port_id] = WaitForMessage();
+-
+-  // Mutate the IPC payload.
+   EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
+   EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
++
++  // Mutate the IPC payload.
+   info.source_endpoint.type = MessagingEndpoint::Type::kExtension;
+ 
+   // Inject the malformed/mutated IPC and verify that the renderer is terminated
+   // as expected.
+   content::RenderProcessHost* main_frame_process =
+-      web_contents->GetPrimaryMainFrame()->GetProcess();
++      active_web_contents()->GetPrimaryMainFrame()->GetProcess();
+   RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
+   IPC::IpcSecurityTestUtil::PwnMessageReceived(
+       main_frame_process->GetChannel(),
+@@ -432,25 +465,23 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
+                        FromContentScript_NoExtensionIdForExtensionType) {
+   // Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
+   // from a content script of an `active_extension_id`.
+-  content::WebContents* web_contents =
+-      browser()->tab_strip_model()->GetActiveWebContents();
+   ASSERT_TRUE(ExecuteProgrammaticContentScript(
+-      web_contents, active_extension_id(),
++      active_web_contents(), active_extension_id(),
+       "chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
+ 
+   // Capture the IPC.
+   auto [source_context, info, channel_name, port_id] = WaitForMessage();
+-
+-  // Mutate the IPC payload.
+   EXPECT_EQ(MessagingEndpoint::Type::kTab, info.source_endpoint.type);
+   EXPECT_EQ(active_extension_id(), info.source_endpoint.extension_id);
++
++  // Mutate the IPC payload.
+   info.source_endpoint.type = MessagingEndpoint::Type::kExtension;
+   info.source_endpoint.extension_id = absl::nullopt;
+ 
+   // Inject the malformed/mutated IPC and verify that the renderer is terminated
+   // as expected.
+   content::RenderProcessHost* main_frame_process =
+-      web_contents->GetPrimaryMainFrame()->GetProcess();
++      active_web_contents()->GetPrimaryMainFrame()->GetProcess();
+   RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
+   IPC::IpcSecurityTestUtil::PwnMessageReceived(
+       main_frame_process->GetChannel(),
+@@ -464,18 +495,16 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
+                        FromContentScript_UnexpectedWorkerContext) {
+   // Trigger sending of a valid ExtensionHostMsg_OpenChannelToExtension IPC
+   // from a content script of an `active_extension_id`.
+-  content::WebContents* web_contents =
+-      browser()->tab_strip_model()->GetActiveWebContents();
+   ASSERT_TRUE(ExecuteProgrammaticContentScript(
+-      web_contents, active_extension_id(),
++      active_web_contents(), active_extension_id(),
+       "chrome.runtime.sendMessage({greeting: 'hello'}, (response) => {});"));
+ 
+   // Capture the IPC.
+   auto [source_context, info, channel_name, port_id] = WaitForMessage();
+-
+-  // Mutate the IPC payload.
+   EXPECT_TRUE(source_context.is_for_render_frame());
+   EXPECT_FALSE(source_context.is_for_service_worker());
++
++  // Mutate the IPC payload.
+   source_context.frame = absl::nullopt;
+   source_context.worker = PortContext::WorkerContext(
+       /* thread_id = */ 123, /* version_id = */ 456,
+@@ -484,7 +513,7 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
+   // Inject the malformed/mutated IPC and verify that the renderer is terminated
+   // as expected.
+   content::RenderProcessHost* main_frame_process =
+-      web_contents->GetPrimaryMainFrame()->GetProcess();
++      active_web_contents()->GetPrimaryMainFrame()->GetProcess();
+   RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
+   IPC::IpcSecurityTestUtil::PwnMessageReceived(
+       main_frame_process->GetChannel(),
+@@ -494,4 +523,98 @@ IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
+             kill_waiter.Wait());
+ }
+ 
++// Native messaging is not available on Fuchsia (i.e.
++// //chrome/browser/extensions/BUILD.gn excludes
++// api/messaging/native_messaging_test_util.h on Fuchsia).
++#if !(BUILDFLAG(IS_FUCHSIA))
++
++// Test suite for covering ExtensionHostMsg_OpenChannelToNativeApp IPC.
++class OpenChannelToNativeAppExploitTest
++    : public ExtensionSecurityExploitBrowserTest {
++ public:
++  OpenChannelToNativeAppExploitTest() = default;
++
++  using OpenChannelMessageWaiter =
++      ExtensionMessageWaiter<ExtensionHostMsg_OpenChannelToNativeApp>;
++  void SetUpOnMainThread() override {
++    // Set up ExtensionMessageWaiter *before* installing the extensions (i.e.
++    // *before* the corresponding RenderProcessHost objects are created).
++    ipc_message_waiter_ = std::make_unique<OpenChannelMessageWaiter>();
++
++    // SetUpOnMainThread in the base class will install the test extensions.
++    ExtensionSecurityExploitBrowserTest::SetUpOnMainThread();
++
++    // Register a (fake, test-only) native messaging host.
++    test_native_messaging_host_.RegisterTestHost(/* user_level= */ false);
++
++    // Navigate the test tab to an extension page.
++    GURL test_page_url = active_extension().GetResourceURL("page.html");
++    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), test_page_url));
++
++    // Only intercept messages from the test process.
++    int matching_process_id =
++        active_web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
++    ipc_message_waiter_->SetIpcMatcher(base::BindLambdaForTesting(
++        [matching_process_id](
++            int captured_render_process_id,
++            const ExtensionHostMsg_OpenChannelToNativeApp::Param& param) {
++          if (captured_render_process_id != matching_process_id)
++            return false;
++
++          return true;
++        }));
++  }
++
++  // Waits for ExtensionHostMsg_OpenChannelToNativeApp IPC and returns its
++  // payload.
++  ExtensionHostMsg_OpenChannelToNativeApp::Param WaitForMessage() {
++    return ipc_message_waiter_->WaitForMessage();
++  }
++
++ private:
++  ScopedTestNativeMessagingHost test_native_messaging_host_;
++  std::unique_ptr<OpenChannelMessageWaiter> ipc_message_waiter_;
++};
++
++IN_PROC_BROWSER_TEST_F(OpenChannelToNativeAppExploitTest,
++                       SourceContextWithSpoofedExtensionId) {
++  // Trigger sending of a valid ExtensionHostMsg_OpenChannelToNativeApp IPC
++  // from a frame of an `active_extension`.
++  const char kScript[] = R"(
++      var message = {text: 'Hello!'};
++      var host = $1;
++      chrome.runtime.sendNativeMessage(host, message);
++  )";
++  ASSERT_EQ(
++      active_extension().origin(),
++      active_web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
++  ASSERT_TRUE(content::ExecuteScript(
++      active_web_contents(),
++      content::JsReplace(kScript, ScopedTestNativeMessagingHost::kHostName)));
++
++  // Capture the IPC.
++  auto [source_context, native_app_name, port_id] = WaitForMessage();
++  EXPECT_EQ(native_app_name, ScopedTestNativeMessagingHost::kHostName);
++  EXPECT_TRUE(source_context.is_for_render_frame());
++
++  // Mutate the IPC payload.
++  source_context = PortContext::ForWorker(123,  // thread_id
++                                          456,  // version_id
++                                          spoofed_extension_id());
++
++  // Inject the malformed/mutated IPC and verify that the renderer is terminated
++  // as expected.
++  content::RenderProcessHost* main_frame_process =
++      active_web_contents()->GetPrimaryMainFrame()->GetProcess();
++  RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
++  IPC::IpcSecurityTestUtil::PwnMessageReceived(
++      main_frame_process->GetChannel(),
++      ExtensionHostMsg_OpenChannelToNativeApp(source_context, native_app_name,
++                                              port_id));
++  EXPECT_EQ(bad_message::EMF_INVALID_EXTENSION_ID_FOR_WORKER_CONTEXT,
++            kill_waiter.Wait());
++}
++
++#endif  // !(BUILDFLAG(IS_FUCHSIA)) - native messaging is available
++
+ }  // namespace extensions
+diff --git a/docs/security/compromised-renderers.md b/docs/security/compromised-renderers.md
+index b7e56be454f2d42dc4ae4ac875586a01f2354d9a..2155a399e0e432fedc2792b6893440efd7fca572 100644
+--- a/docs/security/compromised-renderers.md
++++ b/docs/security/compromised-renderers.md
+@@ -213,14 +213,21 @@ Compromised renderers shouldn’t be able to:
+ - Spoof the `MessageEvent.origin` seen by a recipient of a `postMessage`.
+ - Bypass enforcement of the `targetOrigin` argument of `postMessage`.
+ - Send or receive `BroadcastChannel` messages for another origin.
+-- Spoof the `MessageSender.origin` seen by a recipient of a
+-  `chrome.runtime.sendMessage`
+-  (see also [MessageSender documentation](https://developers.chrome.com/extensions/runtime#type-MessageSender) and [content script security guidance](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-extensions/0ei-UCHNm34)).
++- Spoof the `MessageSender.origin`, nor `MessageSender.id` (i.e. an
++  extension id which can differ from the origin when the message is sent
++  from a content script), as seen by a recipient of a
++  `chrome.runtime.sendMessage`.
++  See also [MessageSender documentation](https://developers.chrome.com/extensions/runtime#type-MessageSender) and [content script security guidance](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-extensions/0ei-UCHNm34).
++- Spoof the id of a Chrome extension initiating
++  [native messaging](https://developer.chrome.com/docs/apps/nativeMessaging/)
++  communication.
+ 
+ Protection techniques:
+ - Using `CanAccessDataForOrigin` to verify IPCs sent by a renderer process
+   (e.g. in `RenderFrameProxyHost::OnRouteMessageEvent` or
+   `BroadcastChannelProvider::ConnectToChannel`).
++- Using `ContentScriptTracker` to check if IPCs from a given renderer process
++  can legitimately claim to act on behalf content scripts of a given extension.
+ 
+ **Known gaps in protection**:
+ - Spoofing of `MessageSender.id` object
+diff --git a/extensions/browser/api/messaging/messaging_api_message_filter.cc b/extensions/browser/api/messaging/messaging_api_message_filter.cc
+index 5469f5e91bfbb98aece208e9fffa8139dc9dacdf..e57cffb46d74229fff2534f7bb9f9fa3ddbc3d26 100644
+--- a/extensions/browser/api/messaging/messaging_api_message_filter.cc
++++ b/extensions/browser/api/messaging/messaging_api_message_filter.cc
+@@ -156,6 +156,16 @@ bool IsValidSourceContext(RenderProcessHost& process,
+     }
+   }
+ 
++  // This function doesn't validate frame-flavoured `source_context`s, because
++  // PortContext::FrameContext only contains frame's `routing_id` and therefore
++  // inherently cannot spoof frames in another process (a frame is identified
++  // by its `routing_id` *and* the `process_id` of the Renderer process hosting
++  // the frame;  the latter is trustworthy / doesn't come from an IPC payload).
++
++  // This function doesn't validate native app `source_context`s, because
++  // `PortContext::ForNativeHost()` is called with trustoworthy inputs (e.g. it
++  // doesn't take input from IPCs sent by a Renderer process).
++
+   return true;
+ }
+ 
+@@ -227,6 +237,18 @@ void MessagingAPIMessageFilter::Shutdown() {
+   shutdown_notifier_subscription_ = {};
+ }
+ 
++content::RenderProcessHost* MessagingAPIMessageFilter::GetRenderProcessHost() {
++  DCHECK_CURRENTLY_ON(BrowserThread::UI);
++  if (!browser_context_)
++    return nullptr;
++
++  // The IPC might race with RenderProcessHost destruction.  This may only
++  // happen in scenarios that are already inherently racey, so returning nullptr
++  // (and dropping the IPC) is okay and won't lead to any additional risk of
++  // data loss.
++  return content::RenderProcessHost::FromID(render_process_id_);
++}
++
+ void MessagingAPIMessageFilter::OverrideThreadForMessage(
+     const IPC::Message& message,
+     BrowserThread::ID* thread) {
+@@ -272,19 +294,14 @@ void MessagingAPIMessageFilter::OnOpenChannelToExtension(
+     const std::string& channel_name,
+     const PortId& port_id) {
+   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+-  if (!browser_context_)
+-    return;
+-
+-  // The IPC might race with RenderProcessHost destruction.  This may only
+-  // happen in scenarios that are already inherently racey, so dropping the IPC
+-  // is okay and won't lead to any additional risk of data loss.
+-  auto* process = content::RenderProcessHost::FromID(render_process_id_);
++  auto* process = GetRenderProcessHost();
+   if (!process)
+     return;
+   TRACE_EVENT("extensions", "MessageFilter::OnOpenChannelToExtension",
+               ChromeTrackEvent::kRenderProcessHost, *process);
+ 
+   ScopedExternalConnectionInfoCrashKeys info_crash_keys(info);
++  debug::ScopedPortContextCrashKeys port_context_crash_keys(source_context);
+   if (!IsValidMessagingSource(*process, info.source_endpoint) ||
+       !IsValidSourceContext(*process, source_context)) {
+     return;
+@@ -303,7 +320,14 @@ void MessagingAPIMessageFilter::OnOpenChannelToNativeApp(
+     const std::string& native_app_name,
+     const PortId& port_id) {
+   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+-  if (!browser_context_)
++  auto* process = GetRenderProcessHost();
++  if (!process)
++    return;
++  TRACE_EVENT("extensions", "MessageFilter::OnOpenChannelToNativeApp",
++              ChromeTrackEvent::kRenderProcessHost, *process);
++
++  debug::ScopedPortContextCrashKeys port_context_crash_keys(source_context);
++  if (!IsValidSourceContext(*process, source_context))
+     return;
+ 
+   ChannelEndpoint source_endpoint(browser_context_, render_process_id_,
+diff --git a/extensions/browser/api/messaging/messaging_api_message_filter.h b/extensions/browser/api/messaging/messaging_api_message_filter.h
+index 6a0ccd698629f650d68f2b4ee168aa2b3b3a116c..3358187387cd9a5765a7bd4e522aeecfd787e06b 100644
+--- a/extensions/browser/api/messaging/messaging_api_message_filter.h
++++ b/extensions/browser/api/messaging/messaging_api_message_filter.h
+@@ -14,6 +14,7 @@ struct ExtensionMsg_TabTargetConnectionInfo;
+ 
+ namespace content {
+ class BrowserContext;
++class RenderProcessHost;
+ }
+ 
+ namespace extensions {
+@@ -40,6 +41,11 @@ class MessagingAPIMessageFilter : public content::BrowserMessageFilter {
+ 
+   void Shutdown();
+ 
++  // Returns the process that the IPC came from, or `nullptr` if the IPC should
++  // be dropped (in case the IPC arrived racily after the process or its
++  // BrowserContext already got destructed).
++  content::RenderProcessHost* GetRenderProcessHost();
++
+   // content::BrowserMessageFilter implementation:
+   void OverrideThreadForMessage(const IPC::Message& message,
+                                 content::BrowserThread::ID* thread) override;
+diff --git a/extensions/common/api/messaging/port_context.cc b/extensions/common/api/messaging/port_context.cc
+index 6872179450d8295de7f15dc1437e9d6edefe4fde..319e2f34eca730c5eb7cf94ef8cdede0ddc3f8e1 100644
+--- a/extensions/common/api/messaging/port_context.cc
++++ b/extensions/common/api/messaging/port_context.cc
+@@ -40,4 +40,27 @@ PortContext PortContext::ForNativeHost() {
+   return PortContext();
+ }
+ 
++namespace debug {
++
++namespace {
++
++base::debug::CrashKeyString* GetServiceWorkerExtensionIdCrashKey() {
++  static auto* crash_key = base::debug::AllocateCrashKeyString(
++      "PortContext-worker-extension_id", base::debug::CrashKeySize::Size64);
++  return crash_key;
++}
++
++}  // namespace
++
++ScopedPortContextCrashKeys::ScopedPortContextCrashKeys(
++    const PortContext& port_context) {
++  if (port_context.is_for_service_worker()) {
++    extension_id_.emplace(GetServiceWorkerExtensionIdCrashKey(),
++                          port_context.worker->extension_id);
++  }
++}
++
++ScopedPortContextCrashKeys::~ScopedPortContextCrashKeys() = default;
++
++}  // namespace debug
+ }  // namespace extensions
+diff --git a/extensions/common/api/messaging/port_context.h b/extensions/common/api/messaging/port_context.h
+index b2e9f057b531d90dc256773959cd586953e4915c..53d94c2ad73c58d45b186a32989e2f4864e67d79 100644
+--- a/extensions/common/api/messaging/port_context.h
++++ b/extensions/common/api/messaging/port_context.h
+@@ -9,6 +9,7 @@
+ 
+ #include <string>
+ 
++#include "base/debug/crash_logging.h"
+ #include "third_party/abseil-cpp/absl/types/optional.h"
+ 
+ namespace extensions {
+@@ -59,6 +60,19 @@ struct PortContext {
+   absl::optional<WorkerContext> worker;
+ };
+ 
++namespace debug {
++
++class ScopedPortContextCrashKeys {
++ public:
++  explicit ScopedPortContextCrashKeys(const PortContext& port_context);
++  ~ScopedPortContextCrashKeys();
++
++ private:
++  absl::optional<base::debug::ScopedCrashKeyString> extension_id_;
++};
++
++}  // namespace debug
++
+ }  // namespace extensions
+ 
+ #endif  // EXTENSIONS_COMMON_API_MESSAGING_PORT_CONTEXT_H_