3

There are a lot of questions on SO similar to this one however none of them solved my purpose.
I'm creating a 'pinterest' like chrome extension. It injects script on webpage, collect images and then post it somewhere. Everything is working perfectly however when i run this on pinterest itself, it gives me this error:

Refused to load the script 'https://domain_name.com/my_script.js' because it violates the following Content Security Policy directive: "default-src 'self' https://.pinterest.com https://.pinimg.com *.pinterest.com *.pinimg.com *.google.com connect.facebook.net .google-analytics.com https://.googleapis.com .gstatic.com https://.facebook.com *.facebook.com www.googleadservices.com googleads.g.doubleclick.net platform.twitter.com *.tiles.mapbox.com *.online-metrix.net *.bnc.lt bnc.lt *.yozio.com 'unsafe-inline' 'unsafe-eval'". Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.


I know this has a lot to do with Content Script Policy (and i don't know much about it) however, i followed this and this link which gave me enough info on what is CSP and how to use it.
I've done everything (what i think) that is required but it still is not working. Here is my manifest.json

{
  "manifest_version": 2,

  "name": "Image Posting",
  "description": "This extension enables you to post images",
  "version": "1.0",

  "browser_action": {
    "name": "Image Posting"
  },
  "homepage_url": "https://www.domain_name.com/",
  "background":{
      "scripts":["background.js"]
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["jquery.js", "content.js"]
    }
  ],
  "icons": {
     "128": "icon_128.png",
     "16": "icon_16.png",
     "48": "icon_48.png"
  },
  "permissions": [
    "activeTab",
    "notifications"
  ],
  "web_accessible_resources": [
    "icon_48.png"
  ],
  "content_security_policy": "default-src 'self'  https://domain_name.com/my_script.js; script-src 'self'  https://domain_name.com/my_script.js; style-src 'self' https://domain_name.com/my_style.css; 'unsafe-inline' 'unsafe-eval'"
}


at a point, i also thought that there might be something in this which cannot be actually done however, i then tried BUFFER extension, and it can successfully inject their script on pinterest as well which means this thing is somehow possible. Also, not to forget that extension like AdBlocker works on every site and they also must be pulling some resources from a remote server. Are they bypassing CSP by any means or there is something really crucial that i don't know or missed. Any suggestions/tips on how to do this?

Tushar Shukla
  • 5,666
  • 2
  • 27
  • 41

4 Answers4

5

There are 3 CSPs at play in extensions:

  • content_security_policy directive only applies for extension's own pages (such as the background page). If not specified, it defaults to script-src 'self'; object-src 'self' and has some restrictions on how it can be modified.

  • Content script context is not subject to this CSP. unsafe-eval is unrestricted (since you can do executeScript with arbitrary code anyway), however, inline script and remote script restrictions just don't apply to content scripts, because:

  • Any script in the DOM of the page, be it inline or <script src="..."> tag is executed in the context of the page itself and is subject to the CSP of the page itself. There is precisely one exception, injecting a <script> /* code */ </script> in the page will bypass inline code restrictions for immediate execution.

What you're seeing is apparently a result of trying to load a remote script by injecting a <script src="https://domain_name.com/my_script.js"> into the page. This is subject to the page's own CSP and fails.

  1. In theory, you could affect the page's own CSP by intercepting and hijacking the response headers using webRequest. However, this is not a good approach: you are generally interfering with security of a third-party page. Nobody is going to be happy about that.

  2. What you can do is load the script in the background page using XHR, and then either:

    • Inject it into the content script context by using chrome.tabs.executeScript({code: ...}), which is not subject to page's CSP;

    • Pass it to the context script so that it can be injected into the page by adding <script>...</script> to the DOM, which bypasses the page's CSP as long as it does not violate it further (by loading more scripts or using eval/inline code).

      P.S.: As Rob W suggests, if you're planning to do this then a content script can use XHR itself (assuming, as you have, https source)

Xan
  • 74,770
  • 16
  • 179
  • 206
  • 1
    You can even load the script via XHR from a content script and inject it in the page, unless the page is a https site and the script is from http, then the request is blocked. But that is good, you should not run arbitrary code on third-party pages, let alone insecure (from http) code in https pages. – Rob W Apr 20 '16 at 08:54
5

Without disabling the CSP, you cannot inject scripts that are not whitelisted.

Extension scripts are exempt from this restriction, so host the file in your extension package, declare it in web_accessible_resources and you should be able to run the script.

var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js'); // In web_accessible_resources
(document.head || document.documentElement).appendChild(s);
s.onload = function() {
    s.remove(); // Clean up, just to be nice.
};
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Hi Rob! Nice alternative if the script _can_ be bundled with the extension. My answer assumes it cannot (has to change often). – Xan Apr 20 '16 at 08:50
  • Tanks for your answer however i'm still stuck with this. I did as you said and i believe this is working fine (there are no errors in this regard yet). In the script that i've added (using your way), i need to call another script like this: loadScript("https://another_script.js", function() { //some stuff }) I tried it doing this like var a=chrome.extension.getURL('another_script.js') and then loadScript(a, function() { //some stuff }) but this is giving me error that getURL is not defined. What's wrong in this case? – Tushar Shukla Apr 20 '16 at 11:11
  • @TusharShukla When you load a script in this way, it runs in the context of the page. If you want to load another script, then you could **synchronously** use `document.currentScript.src` to retrieve the script's base URL and use that to get the URL to another_script.js. Note: Injecting script in page should be avoided if possible. Why can't you load the script via a declared content script? – Rob W Apr 20 '16 at 15:18
  • Was wondering if you can help me with [this question](http://stackoverflow.com/questions/36930597/how-pinterest-extension-store-temporarily-images-from-a-web-page-and-then-acce) as well..? – Tushar Shukla Apr 29 '16 at 11:14
  • @TusharShukla I'll defer the task of answering to [DelightedD0D](https://stackoverflow.com/users/1376624/delightedd0d). His comments under your question show that he understands the topic, and since he already expressed interest in posting an answer I won't intervene. – Rob W Apr 29 '16 at 11:20
0

As @Xan cited, "content_security_policy" is applied to extension pages, it seems you are injecting remote script into current web page, which violates the CSP of current page.

One recommended way is to make a local copy of your remote script, however, if you have a good reason to host it in the server, I would suggest you made an ajax call to your server in background page, then call chrome.tabs.executeScript to inject the code into current page.

Code snippets would look like:

var xhr = new XMLHttpRequest();
xhr.onload = function() {
    chrome.tabs.executeScript(tabId, {"code": xhr.responseText});
xhr.open("GET", SERVER_SCRIPT_URL);
xhr.send();
Haibara Ai
  • 10,703
  • 2
  • 31
  • 47
0

I faced the same issue with google analytics and solved by adding the url "https://www.google-analytics.com/analytics.js" to permission array:

"permissions": ["activeTab", "storage","https://www.google-analytics.com/analytics.js"], 
Radi
  • 6,548
  • 18
  • 63
  • 91