Kaynaklar arası ağ istekleri

Normal web sayfaları, uzak sunuculardan veri göndermek ve almak için fetch() veya XMLHttpRequest API'lerini kullanabilir ancak aynı kaynak politikası ile sınırlıdır. İçerik komut dosyaları, istekleri içerik komut dosyasının yerleştirildiği web kaynağı adına başlatır. Bu nedenle, içerik komut dosyaları da aynı kaynak politikasına tabidir. Uzantı kaynakları bu kadar sınırlı değildir. Bir uzantı hizmet çalışanı veya ön plan sekmesinde yürütülen 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 güvenlik kaynağında bulunur. Uzantı, ek ayrıcalıklar istemeden yüklemesi içindeki kaynakları almak için fetch()'ı çağırabilir. Örneğin, bir uzantı config.json adlı bir JSON yapılandırma dosyası içeriyorsa ve bu dosya config_resources/ klasöründeyse 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ğı dışında bir güvenlik kaynağından (ör. https://www.google.com) içerik istemeye çalışırsa bu, uzantının ana makine izinleri olmadığı sürece kaynaklar arası istek olarak değerlendirilir. Uzantının ana makine izinleri olsa bile, kaynaklar arası istekler içerik komut dosyalarında her zaman bu şekilde değerlendirilir.

Kaynaklar arası izin isteme

Bir uzantının kaynağı 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 ikisini 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/"

Bunlar, aşağıdaki gibi eşleşme kalıpları da olabilir:

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

"https://*/" eşleşme kalıbı, erişilebilen tüm alanlara HTTPS erişimine izin verir. Buradaki 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 yoksayıldığını unutmayın.

Erişimin hem ana makineye hem de şemaya göre verildiğini de 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ı bildirmesi gerekir:

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

Fetch() ve XMLHttpRequest()

fetch(), özellikle hizmet çalışanları için oluşturulmuş olup senkron işlemlerden uzaklaşan daha geniş bir web trendini takip eder. XMLHttpRequest() API, hizmet çalışanı dışındaki uzantılarda desteklenir ve bu API'nin çağrılması, 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çınma

fetch() aracılığıyla alınan kaynakları kullanırken ekran dışı belgeniz, yan paneliniz veya pop-up'ınızın siteler arası komut dosyası çalıştırma saldırısına uğramamasına dikkat etmeniz gerekir. Ö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ı erişimini kaynaklar arası isteklerle sınırlama

Bir içerik komut dosyası adına kaynaklar arası istekler gerçekleştirirken, bir içerik komut dosyasının kimliğine bürünmeye çalışabilecek kötü amaçlı web sayfalarına karşı dikkatli olun. Özellikle içerik komut dosyalarının rastgele bir URL istemesine izin vermeyin.

Bir uzantının, içerik komut dosyasının bir öğenin fiyatını öğrenmesine izin vermek için kaynaklar arası istekte bulunduğunu düşünelim. Güvenli olmayan bir yaklaşım, içerik komut dosyasının arka plan sayfası tarafından getirilecek tam kaynağı belirtmesini sağlamaktır.

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ıdan, uzantının erişebildiği herhangi bir URL'yi getirmesini isteyebilir. Kötü amaçlı bir web sayfası bu tür mesajları sahteleştirip uzantıyı kandırarak kaynaklar arası erişim izni vermeye ikna edebilir.

Bunun yerine, getirilebilecek kaynakları sınırlayan ileti işleyiciler tasarlayın. Aşağıda, tam URL değil yalnızca itemId içerik komut dosyası tarafından sağlanmaktadır.

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 etme

Ayrıca, HTTP üzerinden alınan kaynaklara özellikle dikkat edin. Uzantınız saldırıya açık 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üvenlik politikasını ayarlama

Manifestinize bir 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 makinelerin izin verildiğinden emin olmanız gerekir. Varsayılan politika, ana makinelere bağlantıları kısıtlamasa da connect-src veya default-src yönergelerini açıkça eklerken dikkatli olun.