Permintaan jaringan lintas origin

Halaman web biasa dapat menggunakan fetch() atau XMLHttpRequest API untuk mengirim dan menerima data dari server jarak jauh, tetapi dibatasi oleh kebijakan origin yang sama. Skrip konten memulai permintaan atas nama origin web tempat skrip konten dimasukkan, sehingga skrip konten juga tunduk pada kebijakan origin yang sama. Asal ekstensi tidak begitu terbatas. Skrip yang dieksekusi pada pekerja layanan ekstensi atau tab latar depan dapat berkomunikasi dengan server jarak jauh di luar asalnya, selama ekstensi meminta izin lintas origin.

Asal ekstensi

Setiap ekstensi yang berjalan ada dalam asal keamanannya sendiri yang terpisah. Tanpa meminta hak istimewa tambahan, ekstensi dapat memanggil fetch() untuk mendapatkan resource dalam penginstalannya. Misalnya, jika ekstensi berisi file konfigurasi JSON bernama config.json, dalam folder config_resources/, ekstensi tersebut dapat mengambil konten file seperti ini:

const response = await fetch('/config_resources/config.json');
const jsonData = await response.json();

Jika ekstensi mencoba menggunakan asal keamanan selain asalnya sendiri, misalnya https://www.google.com, browser akan melarangnya kecuali jika ekstensi telah meminta izin lintas origin yang sesuai.

Meminta izin lintas origin

Untuk meminta akses ke server jarak jauh di luar asal ekstensi, tambahkan host, cocokkan pola, atau keduanya ke bagian host_permissions pada file manifes.

{
  "name": "My extension",
  ...
  "host_permissions": [
    "https://www.google.com/"
  ],
  ...
}

Nilai izin lintas origin dapat berupa nama host yang sepenuhnya memenuhi syarat, seperti berikut:

  • "https://www.google.com/"
  • "https://www.gmail.com/"

Atau, parameter tersebut dapat dicocokkan dengan pola, seperti ini:

  • "https://*.google.com/"
  • "https://*/"

Pola yang cocok "https://*/" memungkinkan akses HTTPS ke semua domain yang dapat dijangkau. Perlu diperhatikan bahwa di sini, pola pencocokan mirip dengan pola pencocokan skrip konten, tetapi informasi jalur apa pun setelah host akan diabaikan.

Perhatikan juga bahwa akses diberikan baik oleh host maupun skema. Jika ekstensi menginginkan akses HTTP yang aman dan tidak aman ke host atau kumpulan host tertentu, ekstensi harus mendeklarasikan izin secara terpisah:

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

Fetch() vs. XMLHttpRequest()

fetch() dibuat khusus untuk pekerja layanan dan mengikuti tren web yang lebih luas, jauh dari operasi sinkron. XMLHttpRequest() API didukung di ekstensi di luar pekerja layanan, dan memanggilnya akan memicu pengendali pengambilan pekerja layanan ekstensi. Pekerjaan baru harus mendukung fetch() jika memungkinkan.

Pertimbangan keamanan

Menghindari kerentanan pembuatan skrip lintas situs

Saat menggunakan resource yang diambil melalui fetch(), dokumen di luar layar, panel samping, atau pop-up Anda harus berhati-hati agar tidak menjadi korban pembuatan skrip lintas situs. Khususnya, hindari penggunaan API berbahaya seperti innerHTML. Contoh:

const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = jsonData;
    ...

Sebagai gantinya, pilih API yang lebih aman yang tidak menjalankan skrip:

const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// JSON.parse does not evaluate the attacker's scripts.
let resp = JSON.parse(jsonData);

const response = await fetch("https://api.example.com/data.json");
const jsonData = response.json();
// textContent does not let the attacker inject HTML elements.
document.getElementById("resp").textContent = jsonData;

Membatasi akses skrip konten ke permintaan lintas origin

Saat melakukan permintaan lintas origin atas nama skrip konten, berhati-hatilah untuk menjaga dari halaman web berbahaya yang mungkin mencoba meniru skrip konten. Secara khusus, jangan izinkan skrip konten meminta URL arbitrer.

Pertimbangkan contoh saat ekstensi melakukan permintaan lintas origin agar skrip konten dapat menemukan harga item. Satu pendekatan yang tidak terlalu aman adalah membuat skrip konten menentukan resource yang tepat yang akan diambil oleh halaman latar belakang.

chrome.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.
    }
  }
);
chrome.runtime.sendMessage(
  {
    contentScriptQuery: 'fetchUrl',
    url: `https://another-site.com/price-query?itemId=${encodeURIComponent(request.itemId)}`
  },
  response => parsePrice(response.text())
);

Dalam pendekatan di atas, skrip konten dapat meminta ekstensi untuk mengambil URL apa pun yang dapat diakses oleh ekstensi. Halaman web berbahaya mungkin dapat memalsukan pesan semacam itu dan mengelabui ekstensi agar memberikan akses ke resource lintas origin.

Sebagai gantinya, desain pengendali pesan yang membatasi resource yang bisa diambil. Di bawah ini, hanya itemId yang disediakan oleh skrip konten, bukan URL lengkap.

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.contentScriptQuery == 'queryPrice') {
      const 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.
    }
  }
);
chrome.runtime.sendMessage(
  {contentScriptQuery: 'queryPrice', itemId: 12345},
  price => ...
);

Pilih HTTPS daripada HTTP

Selain itu, berhati-hatilah terhadap resource yang diambil melalui HTTP. Jika ekstensi Anda digunakan pada jaringan yang berbahaya, penyerang jaringan (alias "man-in-the-middle") dapat mengubah respons dan berpotensi menyerang ekstensi Anda. Sebagai gantinya, pilih HTTPS jika memungkinkan.

Menyesuaikan kebijakan keamanan konten

Jika mengubah Kebijakan Keamanan Konten default untuk ekstensi dengan menambahkan atribut content_security_policy ke manifes, Anda harus memastikan bahwa setiap host yang ingin dihubungkan diizinkan. Meskipun kebijakan default tidak membatasi koneksi ke host, berhati-hatilah saat menambahkan perintah connect-src atau default-src secara eksplisit.