• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<h1>Cross-Origin XMLHttpRequest</h1>
2
3
4<p id="classSummary">
5Regular web pages can use the
6<a href="http://www.w3.org/TR/XMLHttpRequest/">XMLHttpRequest</a>
7object to send and receive data from remote servers,
8but they're limited by the
9<a href="http://en.wikipedia.org/wiki/Same_origin_policy">same origin policy</a>.
10Extensions aren't so limited.
11An extension can talk to remote servers outside of its origin,
12as long as it first requests cross-origin permissions.</p>
13
14<h2 id="extension-origin">Extension origin</h2>
15<p>Each running extension exists within its own separate security origin. Without
16requesting additional privileges, the extension can use
17XMLHttpRequest to get resources within its installation. For example, if
18an extension contains a JSON configuration file called <code>config.json</code>,
19in a <code>config_resources</code> folder, the extension can retrieve the file's contents like
20this:</p>
21
22<pre>
23var xhr = new XMLHttpRequest();
24xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
25xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
26xhr.send();
27</pre>
28
29<p>If the extension attempts to use a security origin other than itself,
30say http://www.google.com,
31the browser disallows it
32unless the extension has requested the appropriate cross-origin permissions.
33</p>
34
35<h2 id="requesting-permission">Requesting cross-origin permissions</h2>
36
37<p>By adding hosts or host match patterns (or both) to the
38<a href="declare_permissions">permissions</a> section of the
39<a href="manifest">manifest</a> file, the extension can request access to
40remote servers outside of its origin.</p>
41
42<pre data-filename="manifest.json">
43{
44  "name": "My extension",
45  ...
46  <b>"permissions": [
47    "http://www.google.com/"
48  ]</b>,
49  ...
50}
51</pre>
52
53<p>Cross-origin permission values can be fully qualified host names,
54like these:</p>
55
56<ul>
57  <li> "http://www.google.com/" </li>
58  <li> "http://www.gmail.com/" </li>
59</ul>
60
61<p>Or they can be match patterns, like these:</p>
62
63<ul>
64  <li> "http://*.google.com/" </li>
65  <li> "http://*/" </li>
66</ul>
67
68<p>
69A match pattern of "http://*/" allows HTTP access to all reachable domains.
70Note that here,
71match patterns are similar to <a href="match_patterns">content script
72match patterns</a>,
73but any path information following the host is ignored.</p>
74
75<p>Also note that access is granted both by host and by scheme. If an extension
76wants both secure and non-secure HTTP access to a given host or set
77of hosts, it must declare the permissions separately:</p>
78
79<pre data-filename="manifest.json">
80"permissions": [
81  "http://www.google.com/",
82  "https://www.google.com/"
83]
84</pre>
85
86<h2 id="security-considerations">Security considerations</h2>
87
88<p>
89When using resources retrieved via XMLHttpRequest, your background page should
90be careful not to fall victim to <a
91href="http://en.wikipedia.org/wiki/Cross-site_scripting">cross-site
92scripting</a>.  Specifically, avoid using dangerous APIs such as the below:
93</p>
94<pre data-filename="background.js">
95var xhr = new XMLHttpRequest();
96xhr.open("GET", "http://api.example.com/data.json", true);
97xhr.onreadystatechange = function() {
98  if (xhr.readyState == 4) {
99    // WARNING! Might be evaluating an evil script!
100    var resp = eval("(" + xhr.responseText + ")");
101    ...
102  }
103}
104xhr.send();
105</pre>
106<pre data-filename="background.js">
107var xhr = new XMLHttpRequest();
108xhr.open("GET", "http://api.example.com/data.json", true);
109xhr.onreadystatechange = function() {
110  if (xhr.readyState == 4) {
111    // WARNING! Might be injecting a malicious script!
112    document.getElementById("resp").innerHTML = xhr.responseText;
113    ...
114  }
115}
116xhr.send();
117</pre>
118<p>
119Instead, prefer safer APIs that do not run scripts:
120</p>
121<pre data-filename="background.js">
122var xhr = new XMLHttpRequest();
123xhr.open("GET", "http://api.example.com/data.json", true);
124xhr.onreadystatechange = function() {
125  if (xhr.readyState == 4) {
126    // JSON.parse does not evaluate the attacker's scripts.
127    var resp = JSON.parse(xhr.responseText);
128  }
129}
130xhr.send();
131</pre>
132<pre data-filename="background.js">
133var xhr = new XMLHttpRequest();
134xhr.open("GET", "http://api.example.com/data.json", true);
135xhr.onreadystatechange = function() {
136  if (xhr.readyState == 4) {
137    // innerText does not let the attacker inject HTML elements.
138    document.getElementById("resp").innerText = xhr.responseText;
139  }
140}
141xhr.send();
142</pre>
143<p>
144Additionally, be especially careful of resources retrieved via HTTP.  If your
145extension is used on a hostile network, an network attacker (aka a <a
146href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack">"man-in-the-middle"</a>)
147could modify the response and, potentially, attack your extension.  Instead,
148prefer HTTPS whenever possible.
149</p>
150
151<h3 id="interaction-with-csp">Interaction with Content Security Policy</h3>
152
153<p>
154If you modify the default <a href="contentSecurityPolicy">Content
155Security Policy</a> for apps or extensions by adding a
156<code>content_security_policy</code> attribute to your manifest, you'll need to
157ensure that any hosts to which you'd like to connect are allowed. While the
158default policy doesn't restrict connections to hosts, be careful when explicitly
159adding either the <code>connect-src</code> or <code>default-src</code>
160directives.
161</p>
162