| <h1>Cross-Origin XMLHttpRequest</h1> |
| |
| |
| <p id="classSummary"> |
| Regular web pages can use the |
| <a href="http://www.w3.org/TR/XMLHttpRequest/">XMLHttpRequest</a> |
| object to send and receive data from remote servers, |
| but they're limited by the |
| <a href="http://en.wikipedia.org/wiki/Same_origin_policy">same origin policy</a>. |
| Extensions aren't so limited. |
| An extension can talk to remote servers outside of its origin, |
| as long as it first requests cross-origin permissions.</p> |
| |
| <h2 id="extension-origin">Extension origin</h2> |
| <p>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 <code>config.json</code>, |
| in a <code>config_resources</code> folder, the extension can retrieve the file's contents like |
| this:</p> |
| |
| <pre> |
| var xhr = new XMLHttpRequest(); |
| xhr.onreadystatechange = handleStateChange; // Implemented elsewhere. |
| xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true); |
| xhr.send(); |
| </pre> |
| |
| <p>If the extension attempts to use a security origin other than itself, |
| say http://www.google.com, |
| the browser disallows it |
| unless the extension has requested the appropriate cross-origin permissions. |
| </p> |
| |
| <h2 id="requesting-permission">Requesting cross-origin permissions</h2> |
| |
| <p>By adding hosts or host match patterns (or both) to the |
| <a href="declare_permissions">permissions</a> section of the |
| <a href="manifest">manifest</a> file, the extension can request access to |
| remote servers outside of its origin.</p> |
| |
| <pre data-filename="manifest.json"> |
| { |
| "name": "My extension", |
| ... |
| <b>"permissions": [ |
| "http://www.google.com/" |
| ]</b>, |
| ... |
| } |
| </pre> |
| |
| <p>Cross-origin permission values can be fully qualified host names, |
| like these:</p> |
| |
| <ul> |
| <li> "http://www.google.com/" </li> |
| <li> "http://www.gmail.com/" </li> |
| </ul> |
| |
| <p>Or they can be match patterns, like these:</p> |
| |
| <ul> |
| <li> "http://*.google.com/" </li> |
| <li> "http://*/" </li> |
| </ul> |
| |
| <p> |
| A match pattern of "http://*/" allows HTTP access to all reachable domains. |
| Note that here, |
| match patterns are similar to <a href="match_patterns">content script |
| match patterns</a>, |
| but any path information following the host is ignored.</p> |
| |
| <p>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:</p> |
| |
| <pre data-filename="manifest.json"> |
| "permissions": [ |
| "http://www.google.com/", |
| "https://www.google.com/" |
| ] |
| </pre> |
| |
| <h2 id="security-considerations">Security considerations</h2> |
| |
| <p> |
| When using resources retrieved via XMLHttpRequest, your background page should |
| be careful not to fall victim to <a |
| href="http://en.wikipedia.org/wiki/Cross-site_scripting">cross-site |
| scripting</a>. Specifically, avoid using dangerous APIs such as the below: |
| </p> |
| <pre data-filename="background.js"> |
| var xhr = new XMLHttpRequest(); |
| xhr.open("GET", "http://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(); |
| </pre> |
| <pre data-filename="background.js"> |
| var xhr = new XMLHttpRequest(); |
| xhr.open("GET", "http://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(); |
| </pre> |
| <p> |
| Instead, prefer safer APIs that do not run scripts: |
| </p> |
| <pre data-filename="background.js"> |
| var xhr = new XMLHttpRequest(); |
| xhr.open("GET", "http://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(); |
| </pre> |
| <pre data-filename="background.js"> |
| var xhr = new XMLHttpRequest(); |
| xhr.open("GET", "http://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(); |
| </pre> |
| <p> |
| Additionally, be especially careful of resources retrieved via HTTP. If your |
| extension is used on a hostile network, an network attacker (aka a <a |
| href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack">"man-in-the-middle"</a>) |
| could modify the response and, potentially, attack your extension. Instead, |
| prefer HTTPS whenever possible. |
| </p> |
| |
| <h3 id="interaction-with-csp">Interaction with Content Security Policy</h3> |
| |
| <p> |
| If you modify the default <a href="contentSecurityPolicy">Content |
| Security Policy</a> for apps or extensions by adding a |
| <code>content_security_policy</code> 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 <code>connect-src</code> or <code>default-src</code> |
| directives. |
| </p> |