且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用Chrome扩展程序注入远程iframe

更新时间:2023-12-05 20:09:04

正确的方法是使用 chrome.webRequest 后台脚本中的API并拦​​截HTTP响应。

然后,您可以覆盖响应标头以修改Content-Security-Policy标头。您还可以修改 X-Frame-Options 标题(如果需要)。



文档: chrome.webRequest


For the life of me, I cannot get my Chrome extension to display an iframe with a remote URL.

I see the following message in the console -

Refused to frame 'https://www.example.com/' because it violates the following Content Security Policy directive: "child-src 'self'". Note that 'frame-src' was not explicitly set, so 'child-src' is used as a fallback.

I found a solution on here (Injecting iframe into page with restrictive Content Security Policy), which requires injecting a local iframe which then comatins another iframe that references the remote url. This is supposed to bypass the sontent security policy. But for some reason, it does not seem to work in my case. Am I missing something or has the chrome security policy changed?

Below are portions of my extension that pertain to this issue. Note - this code is not the prettiest as I've been hacking around trying to get this to work.

The way this works right now is background.js sends a message to inject.js. inject.js inserts the first iframe, referencing the local file infobar.html. This page is our main user interface, we want the remote html page displayed in an iframe as part of this page. Then infobar.js inserts an iframe referencing the local file frame.html. Finally, frame.html has an iframe hard coded to our remote url.

Based on the previous answer, only the first iframe should have been subject to the content security policy. However, that doesn't seem to be the case here, as the one referencing example.com is actually 3 iframes deep.

manifest.json

{
...
  "content_security_policy": "script-src 'self'; object-src 'self'; frame-src https://www.example.com; child-src https://www.example.com",
  "background": {
    "scripts": [
      "js/jquery/jquery.min.js",
      "src/bg/background.min.js"
    ],
    "persistent": true
  },
...
  "content_scripts": [
    {
      ...
      "css": [
        ...
        "src/inject/inject.min.css"
      ],
      "js": [
        ...
        "src/inject/inject.min.js"
      ]
    }
  ],
  "externally_connectable": {
    "matches": [
      "*://localhost/*",
      "*://*.example.com/*
    ]
  },
  "web_accessible_resources": [
    "src/inject/inject.html",
    "src/inject/infobar.html",
    "src/inject/infobar.min.js",
    "src/inject/frame.html"
  ],
  "sandbox": {
    "pages": [
      "src/inject/infobar.html",
      "src/inject/frame.html"
    ]
  }
}

inject.js

var iframe = document.createElement("iframe");
iframe.scrolling = "no";
iframe.style.cssText = "display:none;";
...
$(iframe).load(function () {
    var message = {
        command: "render-frame",
        context: data,
        frameUrl: chrome.runtime.getURL("src/inject/frame.html")
    };
    iframe.contentWindow.postMessage(message, '*');
    iframe.style.cssText = "border: 0px; overflow: visible; padding: 0px; right: auto; width: 100%; height: " + toolbarHeight + "px; top: 0px; left: 0px; z-index: 2147483647; box-shadow: rgba(0, 0, 0, 0.498039) 0px 3px 10px; position: fixed; display: none;";
});
...
iframe.src = chrome.runtime.getURL("src/inject/infobar.html");      
...     
document.documentElement.appendChild(iframe);  

infobar.html

Simple HTML page. Nothing pertinent in there. References infobar.js.

infobar.js

window.addEventListener("message", function (event) {
    var command = event.data.command;
    switch (command) {
        case "render-frame":
            var frame = document.createElement("iframe");
            frame.scrolling = "no";
            frame.src = event.data.frameUrl;
            document.getElementById("content").appendChild(frame); 
...
            break;
    }
});

frame.html

<html>
<head>
    <style>
        html, body, iframe, h2 {
            margin: 0;
            border: 0;
            padding: 0;
            display: block;
            width: 100vw;
            height: 100vh;
            background: white;
            color: black;
        }
    </style>
</head>
<body>
<iframe src="https://www.example.com/page.html"></iframe>
</body>
</html>

The proper way is to use the chrome.webRequest API in your background script and intercept HTTP responses.

You can then override response headers to modify Content-Security-Policy header. You can also modify X-Frame-Options header (if required).

Documentation: chrome.webRequest