1. 程式人生 > >在瀏覽器擴充套件程式中進行: 跨域 XMLHttpRequest 請求

在瀏覽器擴充套件程式中進行: 跨域 XMLHttpRequest 請求

跨域 XMLHttpRequest 請求
https://crxdoc-zh.appspot.com/extensions/xhr

普通網頁能夠使用XMLHttpRequest物件傳送或者接受伺服器資料, 但是它們受限於同源策略. 擴充套件可以不受該限制. 任何擴充套件只要它先獲取了跨域請求許可,就可以進行跨域請求。

注意:頁面內容指令碼不能直接發起跨域請求. 然而, 任何一個頁面內容指令碼都可以傳送訊息給父擴充套件,請求父擴充套件發起一次跨域請求。關於使用這一技術的例子,請參照contentscript_xhr example.

擴充套件所屬域

每個正在執行的擴充套件都存在於自己獨立的安全域裡. 當沒有獲取其他許可權時,擴充套件能夠使用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的資源(假設該擴充套件不是來自www.google.com), 瀏覽器不會允許這樣的請求,除非該擴充套件獲得了相應的跨域請求允許。

獲取跨域請求允許
通過新增域名或者域名匹配到manifest檔案的permissions段, 該擴充套件就擁有了訪問除了自己所屬域以外的其他域的訪問許可權.

{
  "name": "My extension",
  ...
  "permissions": [
    "http://www.google.com/"
  ],
  ...
}

跨域允許設定可以使用完整域名, 例如:

"http://www.google.com/"
"http://www.gmail.com/"

或者使用模式匹配, 例如:

"http://*.google.com/"
"http://*/"

模式匹配"http://*/" 表示可以發起到所有域的HTTP請求. 注意在這裡, 模式匹配有點像內容指令碼匹配, 但是這裡的任何域名後的路徑資訊都被忽略

這裡還需要注意訪問許可權是根據訪問協議(匹配模式裡的http或者https或者其他協議名)及域名來授予的. 例如某個擴充套件希望同時基於https和http協議訪問某個域或者某些域, 那麼它必須分別獲取基於這兩種協議的訪問允許(類似下面這樣的宣告):

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

安全性考慮
每當使用通過XMLHttpRequest獲取的資源時, 你編寫的背景頁需要注意不要成為跨域指令碼的犧牲品. 特別注意避免使用像下面這樣的危險API:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // 警告! 這裡有可能執行了一段惡意指令碼!
    var resp = eval("(" + xhr.responseText + ")");
    ...
  }
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // 警告! 這樣處理有可能被注入惡意指令碼!
    document.getElementById("resp").innerHTML = xhr.responseText;
    ...
  }
}
xhr.send();

實際上我們應該首選不會執行指令碼的安全API:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // JSON解析器不會執行攻擊者設計的指令碼.
    var resp = JSON.parse(xhr.responseText);
  }
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // innerText不會給攻擊者注入HTML元素的機會.
    document.getElementById("resp").innerText = xhr.responseText;
  }
}
xhr.send();

另外在使用通過協議HTTP獲取的資源時要特別小心. 如果你開發的擴充套件被應用在惡意網路環境中,網路攻擊者(又叫 "中間人攻擊") 可能篡改伺服器響應內容從而可能攻擊你編寫的擴充套件. 事實上,你應該儘可能地首選使用HTTPS協議.