Normal web sayfaları, uzak sunuculardan veri gönderip almak için fetch()
veya XMLHttpRequest
API'lerini kullanabilir ancak aynı kaynak politikası ile sınırlıdır. İçerik komut dosyaları, içerik komut dosyasının yerleştirildiği web kaynağı adına istekler başlatır. Bu nedenle içerik komut dosyaları da aynı kaynak politikasına tabidir. Uzantının kaynakları o kadar sınırlı değildir. Uzantı hizmet işçisinde veya ön plan sekmesinde çalışan bir komut dosyası, uzantı ana makine izinleri istediği sürece kaynağının dışındaki uzak sunucularla iletişim kurabilir.
Uzantı kaynağı
Çalışan her uzantı, kendi ayrı güvenlik kaynağında bulunur. Uzatma, ek ayrıcalıklar istemeden yükleme sırasındaki kaynakları almak için fetch()
'ü çağırabilir. Örneğin, bir uzantı config_resources/
klasöründe config.json
adlı bir JSON yapılandırma dosyası içeriyorsa uzantı, dosyanın içeriğini şu şekilde alabilir:
const response = await fetch('/config_resources/config.json');
const jsonData = await response.json();
Uzantı, kendi güvenlik kaynağından farklı bir kaynaktan (ör. https://www.google.com) içerik istemeye çalışırsa uzantının barındırma izinleri olmadığı sürece bu istek kaynaklar arası istek olarak değerlendirilir. Uzantı, barındırma izinlerine sahip olsa bile kaynak farklı olan istekler, içerik komut dosyalarında her zaman kaynak farklı olarak değerlendirilir.
Kaynaklar arası izin isteğinde bulunma
Bir uzantının kaynağının dışındaki uzak sunuculara erişim isteğinde bulunmak için manifest dosyasının host_permissions bölümüne ana makineler, eşleşme kalıpları veya her ikisi de ekleyin.
{
"name": "My extension",
...
"host_permissions": [
"https://www.google.com/"
],
...
}
Kaynaklar arası izin değerleri, aşağıdakiler gibi tam nitelikli ana makine adları olabilir:
- "https://www.google.com/"
- "https://www.gmail.com/"
Ya da aşağıdaki gibi eşleşme kalıpları olabilir:
- "https://*.google.com/"
- "https://*/"
"https://*/" eşleşme kalıbı, erişilebilir tüm alanlara HTTPS erişimine izin verir. Burada eşleşme kalıplarının içerik komut dosyası eşleşme kalıplarına benzer olduğunu ancak ana makineden sonraki yol bilgilerinin yok sayıldığını unutmayın.
Ayrıca, erişimin hem ana makine hem de şema tarafından verildiğini unutmayın. Bir uzantı, belirli bir ana makineye veya ana makine grubuna hem güvenli hem de güvenli olmayan HTTP erişimi istiyorsa izinleri ayrı ayrı belirtmelidir:
"host_permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
Fetch() ve XMLHttpRequest()
fetch()
, özellikle hizmet çalışanları için oluşturuldu ve senkronize işlemlerden uzaklaşan daha geniş bir web trendini takip ediyor. XMLHttpRequest()
API, hizmet çalışanının dışındaki uzantılarda desteklenir ve çağrıldığında uzantı hizmet çalışanının getirme işleyicisini tetikler. Mümkün olduğunda yeni çalışmalarda fetch()
tercih edilmelidir.
Güvenlikle ilgili olarak göz önünde bulundurulması gerekenler
Siteler arası komut dosyası çalıştırma güvenlik açıklarından kaçının
fetch()
aracılığıyla alınan kaynakları kullanırken ekran dışı belgeniz, yan paneliniz veya pop-up'ınız siteler arası komut dosyası çalıştırma kurbanı olmamalıdır. Özellikle innerHTML
gibi tehlikeli API'leri kullanmaktan kaçının. Örneğin:
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;
...
Bunun yerine, komut dosyası çalıştırmayan daha güvenli API'leri tercih edin:
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;
İçerik komut dosyasının kaynaklar arası isteklere erişimini sınırlama
Bir içerik komut dosyası adına kaynakta çapraz istek gerçekleştirirken, içerik komut dosyası kimliğine bürünmeye çalışabilecek kötü amaçlı web sayfalarına karşı dikkatli olun. Özellikle içerik komut dosyalarının isteğe bağlı URL istemesine izin vermeyin.
Bir uzantının, içerik komut dosyasının bir öğenin fiyatını keşfetmesine izin vermek için kaynaktan bağımsız istek gerçekleştirdiği bir örneği ele alalım. Çok güvenli olmayan bir yaklaşım, içerik komut dosyasının arka plan sayfası tarafından getirilecek kaynağı tam olarak belirtmesidir.
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())
);
Yukarıdaki yaklaşımda içerik komut dosyası, uzantının erişebildiği herhangi bir URL'yi getirmesini isteyebilir. Kötü amaçlı bir web sayfası bu tür mesajları taklit edebilir ve uzantıyı kaynak dışı kaynaklara erişim izni vermeye kandırabilir.
Bunun yerine, getirilebilecek kaynakları sınırlayan mesaj işleyiciler tasarlayın. Aşağıda, içerik komut dosyası URL'nin tamamını değil, yalnızca itemId
değerini sağlar.
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 => ...
);
HTTP yerine HTTPS'yi tercih edin
Ayrıca, HTTP üzerinden alınan kaynaklara özellikle dikkat edin. Uzantılarınız düşmanca bir ağda kullanılıyorsa ağ saldırganı (diğer adıyla "man-in-the-middle"), yanıtı değiştirebilir ve uzantınıza saldırabilir. Bunun yerine, mümkün olduğunda HTTPS'yi tercih edin.
İçerik güvenliği politikasını ayarlama
Manifestinize content_security_policy
özelliği ekleyerek uzantınızın varsayılan İçerik Güvenliği Politikası'nı değiştirirseniz bağlanmak istediğiniz tüm ana makinelere izin verilmelidir. Varsayılan politika, ana makinelere olan bağlantıları kısıtlamasa da connect-src
veya default-src
yönergelerini açıkça eklerken dikkatli olun.