惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
Scott Helme
Scott Helme
Google Online Security Blog
Google Online Security Blog
L
LINUX DO - 最新话题
O
OpenAI News
S
Secure Thoughts
Cisco Talos Blog
Cisco Talos Blog
Forbes - Security
Forbes - Security
V
Visual Studio Blog
有赞技术团队
有赞技术团队
Jina AI
Jina AI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Microsoft Azure Blog
Microsoft Azure Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Troy Hunt's Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Schneier on Security
雷峰网
雷峰网
The Cloudflare Blog
量子位
Last Week in AI
Last Week in AI
T
Tor Project blog
V
Vulnerabilities – Threatpost
C
Cisco Blogs
B
Blog RSS Feed
S
Security @ Cisco Blogs
T
Threat Research - Cisco Blogs
G
GRAHAM CLULEY
MyScale Blog
MyScale Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Schneier on Security
Schneier on Security
Martin Fowler
Martin Fowler
Help Net Security
Help Net Security
J
Java Code Geeks
人人都是产品经理
人人都是产品经理
G
Google Developers Blog
S
Security Affairs
A
Arctic Wolf
T
Tenable Blog
PCI Perspectives
PCI Perspectives
Spread Privacy
Spread Privacy
AI
AI
L
LangChain Blog
Latest news
Latest news
博客园 - 叶小钗
博客园 - Franky
T
Threatpost
MongoDB | Blog
MongoDB | Blog
Hugging Face - Blog
Hugging Face - Blog

戴兜的小屋

Hi! 2024 - 戴兜的小屋 优雅地实现滚动容器遮罩 - 戴兜的小屋 [简中] Google Summer of Code & Chrome Extensions - 戴兜的小屋 Google Summer of Code & Chrome Extensions - 戴兜的小屋 SCSS+WindiCSS实现主题色切换 - 戴兜的小屋 Plaid CTF Writeup [Treasure Map/CSS] - 戴兜的小屋 写一个炫酷的个人名片页✨✨ - 戴兜的小屋 【戴兜的游戏安利】Hi-Fi RUSH,不可多得的动作爽游 - 戴兜的小屋 在 Nuxt.js 中配合 windicss 实现暗黑模式适配 - 戴兜的小屋 实现滚动时Header自动隐藏 - 戴兜的小屋 你好!2023 - 戴兜的小屋 !important导致TransitionGroup失效 - 戴兜的小屋 新年快乐! - 戴兜的小屋 CSS Layout API初探:瀑布流布局实现 - 戴兜的小屋 你好! 2022 - 戴兜的小屋 手撸一个前端天气卡片 - 戴兜的小屋 Hi!2021 - 戴兜的小屋 服务器又搬迁啦! - 戴兜的小屋 base64数据隐写实现原理分析 - 戴兜的小屋
Manifest V3扩展Content Script绕过CSP限制点击页面内元素 - 戴兜的小屋
戴兜文章作者 · 2023-03-17 · via 戴兜的小屋

背景

在Manifest V3中,谷歌对CSP策略的限制变得更加严格。例如,不允许使用unsafe-inline指令,这避免扩展执行远程代码,然而,这也意味着注入到页面中隔离环境的Content Scripts受到了扩展CSP策略的约束。因此,当页面中的链接包含内联的事件处理器/javascript:伪协议时,如果尝试在Content Scripts中点击链接,将发生错误,如下图所示:

Issue 1299742

在Content Scripts中,操纵页面元素是一个非常常见的需求,如何在保证扩展合法的情况下,正常进行按钮的点击,便变得十分重要。

解决方案

chrome.scripting介绍

为了达成这一目的,Chrome在ManifestV3扩展中提供了动态注入脚本的能力(chrome.scripting)。该接口允许我们将扩展中存在的js文件或文件中的特定函数注入到指定页面中。

// background.js

function someFunc() {
  // 将被注入页面的函数,函数能够与页面交互,例如 document.querySelector("a").remove()
}

// 通过某种方式获取 tabId
let tabId = getTabId();

chrome.scripting.executeScript({
    target: { tabId },
    function: someFunc,
    world: "MAIN",
});

以上是一段示例代码,executeScript方法提供了向指定页面注入脚本的能力(类似于通过Manifest文件注入Content Scripts),该方法包含了名为world的参数,可以设置为ISOLATED和MAIN。通过这个参数,开发者可以自由选择将脚本注入到isolated环境还是main环境中。

isolated环境就是Content Scripts默认注入的环境,在此环境下,Content Scripts能够操作页面、访问页面顶层变量,但原始页面无法读取Content Scripts的内容,并且Content Scripts受到扩展CSP策略的限制。

相反地,被注入到main环境的脚本受到原始页面CSP策略的限制。此外,原始页面可以访问Content Scripts中的变量。

实现方式

有了executeScript方法,我们就可以尝试通过在main环境中执行click来绕过扩展的CSP策略限制。

大概的实现方式如下:

  1. 在isolated环境下的Content Stript中向background发起点击链接的请求,并传递元素选择器
  2. background收到点击链接的请求后,向页面注入一个main环境的脚本用于点击对应的链接

示例代码

// background.js

function clickElement(elementSelector) {
  let el = document.querySelector(elementSelector);
  if (el) {
    el.click();
  }
}

chrome.runtime.onMessage.addListener(function (request, sender) {
  if (request.type === "click") {
    chrome.scripting.executeScript({
      target: { tabId: sender.tab.id },
      function: clickElement,
      args: [request.element],
      world: "MAIN",
    });
  }
});
// content.js (isolated world)

const el = document.querySelector('a[href^="javascript:"]');

const getCssPath = function (el) {
  if (!(el instanceof Element)) return;
  const path = [];
  while (el.nodeType === Node.ELEMENT_NODE) {
    var selector = el.nodeName.toLowerCase();
    if (el.id) {
      selector += "#" + el.id;
      path.unshift(selector);
      break;
    } else {
      let sib = el,
        nth = 1;
      while ((sib = sib.previousElementSibling)) {
        if (sib.nodeName.toLowerCase() == selector) nth++;
      }
      if (nth != 1) selector += ":nth-of-type(" + nth + ")";
    }
    path.unshift(selector);
    el = el.parentNode;
  }
  return path.join(" > ");
};

chrome.runtime.sendMessage({ type: "click", element: getCssPath(el) });