Permintaan jaringan lintas origin

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

Asal ekstensi

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

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

Jika ekstensi mencoba meminta konten dari asal keamanan selain asalnya sendiri, misalnya https://www.google.com, hal ini akan diperlakukan sebagai permintaan lintas asal, kecuali jika ekstensi memiliki izin host. Permintaan lintas asal selalu diperlakukan seperti itu dalam skrip konten, meskipun ekstensi memiliki izin host.

Meminta izin lintas asal

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

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

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

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

Atau, dapat berupa pola kecocokan, seperti berikut:

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

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

Perhatikan juga bahwa akses diberikan berdasarkan host dan 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 yang menjauhi operasi sinkron. XMLHttpRequest() API didukung dalam ekstensi di luar pekerja layanan, dan memanggilnya akan memicu pengendali pengambilan pekerja layanan ekstensi. Pekerjaan baru harus lebih mengutamakan fetch() jika memungkinkan.

Pertimbangan keamanan

Menghindari kerentanan 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 skrip lintas situs. Secara khusus, 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, sebaiknya gunakan API yang lebih aman dan 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 asal

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

Pertimbangkan contoh saat ekstensi melakukan permintaan lintas asal untuk memungkinkan skrip konten menemukan harga item. Salah satu pendekatan yang tidak terlalu aman adalah meminta skrip konten menentukan resource yang tepat untuk 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. Laman web berbahaya mungkin dapat membuat pesan tersebut dan menipu ekstensi untuk memberikan akses ke resource lintas asal.

Sebagai gantinya, desain pengendali pesan yang membatasi resource yang dapat diambil. Di bawah, 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 => ...
);

Lebih memilih HTTPS daripada HTTP

Selain itu, berhati-hatilah dengan resource yang diambil melalui HTTP. Jika ekstensi Anda digunakan di jaringan yang tidak aman, penyerang jaringan (juga dikenal sebagai "man-in-the-middle") dapat mengubah respons dan, berpotensi, menyerang ekstensi Anda. Sebagai gantinya, sebaiknya gunakan HTTPS jika memungkinkan.

Menyesuaikan kebijakan keamanan konten

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