通常のウェブページは XMLHttpRequest オブジェクトを使ってリモートホストとの通信を行う。しかしそこには 同一生成元ポリシー という制限が存在する。拡張機能では、そのような制限は設けていない。拡張機能は最初に生成元横断のパーミッションを要求しておくことで、異なる生成元のサーバとも通信することが出来る。
実行されている各拡張機能は、それぞれ分割された生成元に存在している。特別に特権を要求しなくても、XMLHttpRequestを使って自身のインストールフォルダからのリソース取得を行うことが出来る。例えば、拡張機能が config.json というJSONで記述された設定ファイルを config_resources というフォルダに持っている場合、次のように取得することが出来る。
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();
もし拡張機能が自分自身ではない生成元、例えば http://www.google.com などを使おうとすれば、事前に適切な生成元横断のパーミッションが要求されていない限り、ブラウザはそれを許可しない。
Manifest Fileの permissions セクションにホスト名やホスト名のマッチパターンを記述することで、拡張機能は生成元を超えてリモートサーバにアクセス要求を送ることが可能になる。
{
"name": "My extension",
...
"permissions": [
"http://www.google.com/"
],
...
}
生成元横断パーミッションの値には、次のように正確なホスト名を指定することが出来る。
また、次のようにマッチパターンによる指定も可能。
例にある “http://*/” というパターンは、つまり全てのドメインへHTTPでのアクセスを許可するという意味になる。注意が必要なのは、マッチパターンはコンテント・スクリプトのマッチパターンとほぼ同じ記述であるが、ホストより下のパス部分は無視されるということである。
もうひとつの注意点として、アクセスはホストとスキームの両方で確認されるということ。もし拡張機能が http および https 両方でのアクセスを行いたければ、それぞれパーミッションを記述すなければならない。
"permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
XMLHttpRequestにより取得したリソースを使う場合、バックグラウンド・ページが XSS の被害にあわないよう注意する必要がある。特に次に示すような危険なAPIは仕様を避けるようにすべき。
/* background */
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// 危険! 悪意のあるスクリプトが実行されてしまうかもしれない
var resp = eval("(" + xhr.responseText + ")");
...
}
}
evalはコードを実行してしまうので、JSONのパースは次のようにすべき。
/* background */
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// JSON.parse であれば、スクリプトは実行されない
var resp = JSON.parse(xhr.responseText);
}
}
/* background */
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// 危険! 悪意あるスクリプトが実行されるかもしれない
document.getElementById("resp").innerHTML = xhr.responseText;
...
}
}
HTMLとしてではなく、テキストとして評価させれば安全。
/* background */
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// innerText であれば、スクリプトが含まれていても実行されない
document.getElementById("resp").innerText = xhr.responseText;
}
}