Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting remote iframe with Chrome extension

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>
like image 936
Phil Figgins Avatar asked Nov 20 '17 15:11

Phil Figgins


People also ask

Can we use iFrame in Chrome extension?

No, the content scripts will NOT execute in the iframes loaded dynamically via JavaScript in the page.

Does Google Chrome support iframes?

iFrame is not working in Chrome but works in Firefox Google Chrome has a different set of rules when it comes to iFrame and it often blocks the content although it works fine on other browsers. You should also try clicking the shield in the URL title bar because that would also help you see the content.

How do I view iFrame in Chrome?

We can detect if an element is inside an iframe by inspecting the element with the Chrome Developer Tools. An easier way is to perform a Right Click near the element in your browser and see if View Frame Source option is present in the context dropdown.


1 Answers

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

like image 73
Usama Ejaz Avatar answered Oct 26 '22 05:10

Usama Ejaz