Is it possible to load a remote webpage into a background page using a chrome extension?
"background": {
"page": "local.html"
}
works, but
"background": {
"page": "http://...."
}
fails with the following error:
Could not load background page http://....
No, that's not possible. It is possible since Chrome 22 - see the bottom of the answer.
You can whitelist a https: resource in the manifest file file, so that your background script can manually be constructed. Make sure that you include a fallback resource in your extension, in the case that the network is down:
<!-- ... doctype etc ... (background.html) -->
<script src="https://..../external_bg.js"></script>
<script src="bg.js"></script>
Because of the Content security policy (CSP), you cannot run inline JavaScript, so you have to use external JS files. bg.js may look like:
if (!window.namespace_of_external_bg) {
// Fallback, by defining fallback methods or injecting a new script:
document.write('<script src="fallback_bg.js"></script>');
}
If you want to dynamically construct a page, avoid use of eval-like methods, because these are also forbidden by the CSP. You can write a template, and request external values to populate your template. localStorage can be used to cache variables. For an example on caching external resources, see Chrome extension adding external javascript to current page's html. This answer referred to Content scripts, so the exact method cannot be used to enable caching scripts (because you would need to use eval to load the script). However, the caching technique can still be used.
I have also tried to use the following method, which does not work (included in this answer, so that you don't have to try it yourself):
Create a Blob from the AJAX response, then use webkitURL.createObjectURL to create a temporary URL to load the resource.
// Modification of https://stackoverflow.com/a/10371025
// Instead of `chrome.tabs.executeScript`, use
// x.responseText or x.response (eg when using x.responseType='arraybuffer')
var blob = new Blob([x.responseText], {type: 'application/javascript'});
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var s = document.createElement('script');
s.src = url;
document.head.appendChild(s);
The previous code yields the following error:
Refused to load the script 'blob:chrome-extension%3A//damgmplfpicjkeogacmlgiceidmilllf/96356d24-3680-4188-812e-5661d23e81df' because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:".
Loading external resources in the background page
Since Chrome 22, it is technically possible (using the unsafe-eval CSP policy) to load non-https resources in the background page. This obviously not recommended because of security concerns (because it's susceptible to the MITM attack, for instance).
Here's an example to load an arbitrary resource and run it in the context of the background script.
function loadScript(url) {
var x = new XMLHttpRequest();
x.onload = function() {
eval(x.responseText); // <---- !!!
};
x.open('GET', url);
x.send();
}
// Usage:
loadScript('http://badpractic.es/insecure.js');
The unsafe-eval CSP policy must be specified.
As usual, to make cross-origin requests, the URL must be whitelisted in the manifest at the permissions section, or the server must enable CORS.
So, the manifest should at least contain:
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"permissions": ["http://badpractic.es/insecure.js"],
"background": {"scripts": ["background.js"] }
Related
I'm trying to execute this js code from an extension that modifies your new tab page.
<script>
function myFunction() {
document.getElementById("qnote-text").innerHTML = "⭕ ";
}
</script>
But I always get the same error on chrome console:
Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self'. Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.
It has to be inline.
your error message said google-chrome extension does not allow inline script. so, you have to do external script like <script src="script.js"></script>
I'm implementing Content Security Policy (CSP) on a site. Below is the CSP policy that I'm testing.
Content-Security-Policy: script-src 'self' 'nonce-random' 'strict-dynamic';
The site uses a third party js script library. The script library injects dynamic content on a page. The dynamic content has inline event handlers. Below is a simple HTML page with a script that mimics the site + the third party library's behavior.
<!DOCTYPE html>
<html>
<head>
<title>CSP Test Page</title>
<script nonce="random">
document.addEventListener('DOMContentLoaded', function (event) {
var el = document.createElement('button');
el.innerHTML = 'Click Me';
el.setAttribute('onclick', "doSomething()");
document.body.appendChild(el);
});
function doSomething() {
alert('I did something.');
}
</script>
</head>
<body>
</body>
</html>
The inline event handlers on the dynamically added button triggers the following error message in the Chrome console:
Refused to execute inline event handler because it violates the
following Content Security Policy directive: "script-src 'self'
'nonce-random' 'strict-dynamic'". Either the 'unsafe-inline' keyword,
a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable
inline execution.
Any suggestions on how to address this issue from a CSP standpoint? I can't change the code of the third party library that is adding the dynamically generated content with the inline event handler.
CSP blocks all inline event handlers, including code added by the third-party library, so unfortunately there is no simple way to solve this without refactoring the CSP-incompatible dependency.
In the long term, CSP3 might provide the ability to whitelist trusted scripts inside event handlers via the 'unsafe-hashed-attributes' feature, but this isn't yet shipping in any stable browser.
In the meantime, one possible workaround is manually removing the attribute with the inline event handler after invoking the external library. That is, you could do something like:
var el = document.createElement('button');
library.doStuff(el);
// Now that the library has run we will change the DOM to be compatible with CSP.
el.onclick = function() { doSomething() };
el.removeAttribute('onclick');
Note that assigning a function to the onclick property directly is okay when it comes to CSP, as opposed to setting the onclick attribute on the HTML element, which is blocked by CSP because it converts a string to code. This will work and avoid CSP violations, but it only makes sense if there is a small number of instances of inline event handlers in the library; otherwise this could get quite tedious.
As a side note, your CSP might benefit from fallbacks for older browsers which don't support 'strict-dynamic', similar to this example.
I have an iframe in a webview which loads a script from the android app's asset using the following :-
<script src='file:///android_asset/trusted-iframe-script.js'></script>
Now I want to have a content security policy on the iframe such that no other script can be loaded.
For this I added the following to the iframe CSP
script-src: 'file:///android_asset/trusted-iframe-script.js';
This doesn't work since the file uri is ignored by chrome.
The source list for Content Security Policy directive 'script-src' contains an invalid source: 'file:///android_asset/trusted-iframe-script.js'. It will be ignored.
Refused to load the script 'file:///android_asset/trusted-iframe-script.js' because it violates the following Content Security Policy directive: "script-src file:///android_asset/trusted-iframe-script.js".
I read about filesystem uri, but that requires requesting access to the user but I actually only need access to my own assets and not filesystem in general. I also read about blob: urls but that feels akin to inlining the whole script
What is the right way to csp restrict to only file urls ?
CSP URI's don't have quote marks - so try script-src: file:///android_asset/trusted-iframe-script.js;
If that doesn't work most Android browsers now support CSP2, which lets you specify a hash for a supported script.
If the CSP is set on the "page" which contains the iframe you should use
the child-src: directive instead of script-src: (Source)
Then, i am not use how to include an assert file and you could try with:
child-src: file:///android_asset/trusted-iframe-script.js
// i cannot test it
child-src: filesystem:///android_asset/trusted-iframe-script.js
// source and check if still requires requesting access to the user
child-src: https://your.trusted.website.com/trusted-iframe-script.js
// this should work but it requires to have a trusted server and the app must connect to the web (no obvious condition)
In short, this cannot be done for a sandboxed iframe.
Chrome CSP doesn't allow file urls to be whitelisted as a script src. You could use the directive file: (without any url) and that would work if the iframe wasn't sandboxed. But this is a bad idea, since
A. my iframe is sandboxed, and
B. this is an undocumented keyword which may stop working at anytime.
I also tried creating a blob url for the content and instead passing that to the iframe but that too doesn't work unless you set allow-same-origin on the iframe sandbox attribute.
I am writing a chrome extension that injects an iframe in the open tab and loads a url in it.The url to be loaded is not on the same domain as the opened page in the tab is.I am using following code :
--menifest.json--
"background" : {
"scripts": ["background.js"]
},
"permissions": [
"tabs", "http://*/", "https://*/"
]
--background.js--
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null,
{file:"logic.js"});
});
--logic.js--
var newdiv = document.createElement('div');
var iframe = document.createElement('iframe');
iframe.setAttribute('src','http://google.co.in');
newdiv.appendChild(iframe);
document.body.appendChild(newdiv);
this works only when the curent page is http://google.co.in and not on other pages.So I am hitting cross domain issues.but as far as I know,extensions can make the cross domain requests,then how to do it?Please help.
Google uses X-Frame-Options header, many websites make use of them as a best practise
There are three possible values for X-Frame-Options:
DENY
The page cannot be displayed in a frame, regardless of the site attempting to do so.
SAMEORIGIN
The page can only be displayed in a frame on the same origin as the page itself.
ALLOW-FROM uri
The page can only be displayed in a frame on the specified origin.
Google uses SAMEORIGIN Value, so this works only when the curent page is http://google.co.in.
So, you are not hitting cross domain issues and Yes extensions can make the cross domain requests.
I'm new to Chrome extension development, I need to make something sample with extension.
I Need to add JavaScript file to all sites I browse when the extension enabled, this is the code i want to add to all pages
<script type="text/javascript" src="web-retina-emulator.js"></script>
This file make pages look like when the website on retina display.
Is there easy way to make this happen?
You can use this basic structure to add JavaScript file to all sites when the extension enabled.
Method 1
If web-retina-emulator.js is an individual file which do not use global variables or functions of pages where it is injected it is suggested to use this method
Advantage:
It has access to certain sections of chrome API*
Draw Back
It can not use javascript variables and functions of pages where it is injected.
Demonstration
manifest.json
{
"name":"Custom Script",
"description":"http://stackoverflow.com/questions/14165629/add-javascript-file-to-all-sites-i-browse",
"version":"1",
"manifest_version":2,
"content_scripts":[{
"matches":["<all_urls>"],
"js":["web-retina-emulator.js"],
}
]
}
Method 2
If web-retina-emulator.js need some javascript methods or variables of pages, use this approach
Advantage:
It has access to javascript variables and methods of pages
Draw Back
It can not use chrome API*.
Demonstration
manifest.json
{
"name":"Custom Script",
"description":"http://stackoverflow.com/questions/14165629/add-javascript-file-to-all-sites-i-browse",
"version":"1",
"manifest_version":2,
"content_scripts":[{
"matches":["<all_urls>"],
"js":["myscript.js"],
}
]
}
myscript.js
var script = document.createElement('script'); // Create a Script Tag
script.src = chrome.extension.getURL("web-retina-emulator.js"); //Fetch the content script
script.onload = function () {
this.parentNode.removeChild(this); //Remove script after script executed
};
(document.head || document.documentElement).appendChild(script); //ADD script tag
//to head or Html element
Method 3
Inserting code into a page programmatically is useful when your JavaScript or CSS code shouldn't be injected into every single page that matches the pattern — for example, if you want a script to run only when the user clicks a browser action's icon.
Demonstration
background.html
chrome.tabs.executeScript(null,
{file:"web-retina-emulator.js"});
manifest.json
Ensure permissions are set in manifest file
"permissions": [
"tabs", "http://*/*"
],
References
Content Scripts
Execute Script API
Check out tampermonkey. It's the chrome equivalent to greasemonkey.