Javascript xmlhttprequest chrome extension

chrome.scripting

To use the chrome.scripting API, declare the «scripting» permission in the manifest plus the host permissions for the pages to inject scripts into. Use the «host_permissions» key or the activeTab permission, which grants temporary host permissions. The following example uses the activeTab permission.

 
"name": "Scripting Extension",
"manifest_version": 3,
"permissions": ["scripting", "activeTab"],
.
>

# Usage

You can use the chrome.scripting API to inject JavaScript and CSS into websites. This is similar to what you can do with content scripts. But by using the chrome.scripting namespace, extensions can make decisions at runtime.

# Injection targets

You can use the target parameter to specify a target to inject JavaScript or CSS into.

The only required field is tabId . By default, an injection will run in the main frame of the specified tab.

function getTabId()  . >

chrome.scripting
.executeScript(
target : tabId : getTabId()>,
files : [ "script.js" ],
>)
.then(() => console.log("script injected"));

To run in all frames of the specified tab, you can set the allFrames boolean to true .

function getTabId()  . >

chrome.scripting
.executeScript(
target : tabId : getTabId(), allFrames : true>,
files : [ "script.js" ],
>)
.then(() => console.log("script injected in all frames"));

You can also inject into specific frames of a tab by specifying individual frame IDs. For more information on frame IDs, see the chrome.webNavigation API.

function getTabId()  . >

chrome.scripting
.executeScript(
target : tabId : getTabId(), frameIds : [ frameId1, frameId2 ]>,
files : [ "script.js" ],
>)
.then(() => console.log("script injected on target frames"));

You cannot specify both the frameIds and allFrames properties.

# Injected code

Extensions can specify the code to be injected either via an external file or a runtime variable.

# Files

Files are specified as strings that are paths relative to the extension’s root directory. The following code will inject the file script.js into the main frame of the tab.

function getTabId()  . >

chrome.scripting
.executeScript(
target : tabId : getTabId()>,
files : [ "script.js" ],
>)
.then(() => console.log("injected script file"));

# Runtime functions

When injecting JavaScript with scripting.executeScript() , you can specify a function to be executed instead of a file. This function should be a function variable available to the current extension context.

function getTabId()  . >
function getTitle() return document.title; >

chrome.scripting
.executeScript(
target : tabId : getTabId()>,
func : getTitle,
>)
.then(() => console.log("injected a function"));
function getTabId()  . >
function getUserColor() . >

function changeBackgroundColor()
document.body.style.backgroundColor = getUserColor();
>

chrome.scripting
.executeScript(
target : tabId : getTabId()>,
func : changeBackgroundColor,
>)
.then(() => console.log("injected a function"));

You can work around this by using the args property:

function getTabId()  . >
function getUserColor() . >
function changeBackgroundColor(backgroundColor)
document.body.style.backgroundColor = backgroundColor;
>

chrome.scripting
.executeScript(
target : tabId : getTabId()>,
func : changeBackgroundColor,
args : [ getUserColor() ],
>)
.then(() => console.log("injected a function"));

# Runtime strings

If injecting CSS within a page, you can also specify a string to be used in the css property. This option is only available for scripting.insertCSS() ; you can’t execute a string using scripting.executeScript() .

function getTabId()  . >
const css = "body < background-color: red; >";

chrome.scripting
.insertCSS(
target : tabId : getTabId()>,
css : css,
>)
.then(() => console.log("CSS injected"));

# Handling results

The results of executing JavaScript are passed to the extension. A single result is included per-frame. The main frame is guaranteed to be the first index in the resulting array; all other frames are in a non-deterministic order.

function getTabId()  . >
function getTitle() return document.title; >

chrome.scripting
.executeScript(
target : tabId : getTabId(), allFrames : true>,
func : getTitle,
>)
.then(injectionResults =>
for (const frameId, result> of injectionResults)
console.log(`Frame $frameId> result:`, result);
>
>);

scripting.insertCSS() does not return any results.

# Promises

If the resulting value of the script execution is a promise, Chrome will wait for the promise to settle and return the resulting value.

function getTabId()  . >
async function addIframe()
const iframe = document.createElement("iframe");
const loadComplete =
new Promise(resolve => iframe.addEventListener("load", resolve));
iframe.src = "https://example.com";
document.body.appendChild(iframe);
await loadComplete;
return iframe.contentWindow.document.title;
>

chrome.scripting
.executeScript(
target : tabId : getTabId(), allFrames : true>,
func : addIframe,
>)
.then(injectionResults =>
for (const frameResult of injectionResults)
const frameId, result> = frameResult;
console.log(`Frame $frameId> result:`, result);
>
>);

# Examples

# Unregister all dynamic content scripts

The following snippet contains a function that unregisters all dynamic content scripts the extension has previously registered.

async function unregisterAllDynamicContentScripts()  
try
const scripts = await chrome.scripting.getRegisteredContentScripts();
const scriptIds = scripts.map(script => script.id);
return chrome.scripting.unregisterContentScripts(scriptIds);
> catch (error)
const message = [
"An unexpected error occurred while",
"unregistering dynamic content scripts.",
].join(" ");
throw new Error(message, cause : error>);
>
>

Unregistering content scripts will not remove scripts or styles that have already been injected.

To try the chrome.scripting API, install the scripting sample from the chrome-extension-samples repository.

Источник

Javascript xmlhttprequest chrome extension

Published on Tuesday, September 18, 2012 • Updated on Monday, March 9, 2020

The Chrome Web Store no longer accepts Manifest V2 extensions. Please use Manifest V3 when building new extensions. You will find a section on upgrading in the navigation tree at the left, including the Manifest V2 support timeline.

Regular web pages can use the XMLHttpRequest object to send and receive data from remote servers, but they’re limited by the same origin policy. Content scripts initiate requests on behalf of the web origin that the content script has been injected into and therefore content scripts are also subject to the same origin policy. (Content scripts have been subject to CORB since Chrome 73 and CORS since Chrome 83.) Extension origins aren’t so limited — a script executing in an extension’s background page or foreground tab can talk to remote servers outside of its origin, as long as the extension requests cross-origin permissions.

# Extension origin

Each running extension exists within its own separate security origin. Without requesting additional privileges, the extension can use XMLHttpRequest to get resources within its installation. For example, if an extension contains a JSON configuration file called config.json , in a config_resources folder, the extension can retrieve the file’s contents like this:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();

If the extension attempts to use a security origin other than itself, say https://www.google.com, the browser disallows it unless the extension has requested the appropriate cross-origin permissions.

# Requesting cross-origin permissions

By adding hosts or host match patterns (or both) to the permissions section of the manifest file, the extension can request access to remote servers outside of its origin.

 
"name": "My extension",
.
"permissions": [
"https://www.google.com/"
],
.
>

Cross-origin permission values can be fully qualified host names, like these:

Or they can be match patterns, like these:

A match pattern of «https://*/» allows HTTPS access to all reachable domains. Note that here, match patterns are similar to content script match patterns, but any path information following the host is ignored.

Also note that access is granted both by host and by scheme. If an extension wants both secure and non-secure HTTP access to a given host or set of hosts, it must declare the permissions separately:

"permissions": [ 
"http://www.google.com/",
"https://www.google.com/"
]

# Security considerations

# Avoiding cross-site scripting vulnerabilities

When using resources retrieved via XMLHttpRequest, your background page should be careful not to fall victim to cross-site scripting. Specifically, avoid using dangerous APIs such as the below:

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function()
if (xhr.readyState == 4)
// WARNING! Might be evaluating an evil script!
var resp = eval("(" + xhr.responseText + ")");
.
>
>
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function()
if (xhr.readyState == 4)
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = xhr.responseText;
.
>
>
xhr.send();

Instead, prefer safer APIs that do not run scripts:

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function()
if (xhr.readyState == 4)
// JSON.parse does not evaluate the attacker's scripts.
var resp = JSON.parse(xhr.responseText);
>
>
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function()
if (xhr.readyState == 4)
// innerText does not let the attacker inject HTML elements.
document.getElementById("resp").innerText = xhr.responseText;
>
>
xhr.send();

# Limiting content script access to cross-origin requests

When performing cross-origin requests on behalf of a content script, be careful to guard against malicious web pages that might try to impersonate a content script. In particular, do not allow content scripts to request an arbitrary URL.

Consider an example where an extension performs a cross-origin request to let a content script discover the price of an item. One (insecure) approach would be to have the content script specify the exact resource to be fetched by the background page.

.runtime.onMessage.addListener( 
function(request, sender, sendResponse)
if (request.contentScriptQuery == 'fetchUrl')
// WARNING: SECURITY PROBLEM - a malicious web page may abuse
// the message handler to get access to arbitrary cross-origin
// resources.
fetch(request.url)
.then(response => response.text())
.then(text => sendResponse(text))
.catch(error => . )
return true; // Will respond asynchronously.
>
>);
.runtime.sendMessage( 
contentScriptQuery: 'fetchUrl',
url: 'https://another-site.com/price-query?itemId=' +
encodeURIComponent(request.itemId)>,
response => parsePrice(response.text()));

In the approach above, the content script can ask the extension to fetch any URL that the extension has access to. A malicious web page may be able to forge such messages and trick the extension into giving access to cross-origin resources.

Instead, design message handlers that limit the resources that can be fetched. Below, only the itemId is provided by the content script, and not the full URL.

.runtime.onMessage.addListener( 
function(request, sender, sendResponse)
if (request.contentScriptQuery == 'queryPrice')
var url = 'https://another-site.com/price-query?itemId=' +
encodeURIComponent(request.itemId);
fetch(url)
.then(response => response.text())
.then(text => parsePrice(text))
.then(price => sendResponse(price))
.catch(error => . )
return true; // Will respond asynchronously.
>
>);
.runtime.sendMessage( 
contentScriptQuery: 'queryPrice', itemId: 12345>,
price => . );

# Preferring HTTPS over HTTP

Additionally, be especially careful of resources retrieved via HTTP. If your extension is used on a hostile network, an network attacker (aka a «man-in-the-middle») could modify the response and, potentially, attack your extension. Instead, prefer HTTPS whenever possible.

# Adjusting the content security policy

If you modify the default Content Security Policy for apps or extensions by adding a content_security_policy attribute to your manifest, you’ll need to ensure that any hosts to which you’d like to connect are allowed. While the default policy doesn’t restrict connections to hosts, be careful when explicitly adding either the connect-src or default-src directives.

Updated on Monday, March 9, 2020 • Improve article

Источник

Читайте также:  Блочная верстка в HTML5
Оцените статью