Halaman web biasa dapat menggunakan objek XMLHttpRequest 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 dan oleh karena itu skrip konten juga tunduk pada kebijakan origin yang sama. (Skrip konten telah tunduk pada CORB sejak Chrome 73 dan CORS sejak Chrome 83.) Asal ekstensi tidak terlalu terbatas - skrip yang dieksekusi di halaman latar belakang atau tab latar depan ekstensi dapat berkomunikasi dengan server jarak jauh di luar asalnya, selama ekstensi meminta izin lintas asal.
Origin ekstensi
Setiap ekstensi yang berjalan ada dalam origin keamanan terpisah miliknya sendiri. Tanpa meminta hak istimewa tambahan, ekstensi dapat menggunakan XMLHttpRequest untuk mendapatkan resource dalam penginstalannya. Misalnya, jika ekstensi berisi file konfigurasi JSON bernama config.json, di folder config_resources, ekstensi dapat mengambil konten file seperti ini:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();
Jika ekstensi mencoba menggunakan origin keamanan selain dirinya sendiri, misalnya https://www.google.com, browser akan melarangnya kecuali jika ekstensi telah meminta izin lintas origin yang sesuai.
Meminta izin lintas origin
Dengan menambahkan host atau pola kecocokan host (atau keduanya) ke bagian permissions pada file manifest, ekstensi dapat meminta akses ke server jarak jauh di luar asalnya.
{
"name": "My extension",
...
"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, pola ini dapat berupa pola kecocokan, seperti berikut:
- "https://*.google.com/"
- "https://*/"
Pola kecocokan "https://*/" mengizinkan akses HTTPS ke semua domain yang dapat dijangkau. Perhatikan 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 berdasarkan host dan skema. Jika ekstensi menginginkan akses HTTP yang aman dan tidak aman ke host atau sekumpulan host tertentu, ekstensi harus mendeklarasikan izin secara terpisah:
"permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
Pertimbangan keamanan
Menghindari kerentanan pembuatan skrip lintas situs
Saat menggunakan resource yang diambil melalui XMLHttpRequest, halaman latar belakang Anda harus berhati-hati agar tidak menjadi korban cross-site scripting. Secara khusus, hindari penggunaan API berbahaya seperti di bawah ini:
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be evaluating an evil script!
var resp = eval("(" + xhr.responseText + ")");
...
}
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = xhr.responseText;
...
}
}
xhr.send();
Sebagai gantinya, pilih API yang lebih aman yang tidak menjalankan skrip:
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// JSON.parse does not evaluate the attacker's scripts.
var resp = JSON.parse(xhr.responseText);
}
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// innerText does not let the attacker inject HTML elements.
document.getElementById("resp").innerText = xhr.responseText;
}
}
xhr.send();
Membatasi akses skrip konten ke permintaan lintas origin
Saat melakukan permintaan lintas origin atas nama skrip konten, berhati-hatilah untuk melindungi diri 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. Salah satu pendekatan (tidak aman) adalah membuat skrip konten menentukan resource persis 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 ekstensi. Halaman web berbahaya mungkin dapat memalsukan pesan tersebut dan menipu ekstensi agar memberikan akses ke resource lintas origin.
Sebagai gantinya, desain handler 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') {
var 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 (alias "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 aplikasi atau ekstensi dengan menambahkan atribut
content_security_policy ke manifes, Anda harus memastikan bahwa semua 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.