Kaynaklar arası ağ istekleri

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 istek başlatır. Bu nedenle içerik komut dosyaları da aynı kaynak politikasına tabidir. Uzantı kaynakları çok sınırlı değildir. Bir uzantı hizmet çalışanı veya ön plan sekmesinde çalışan bir komut dosyası, uzantı, kaynaklar arası izinler 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ğı içinde bulunur. Uzantı, ek ayrıcalık istemeden yükleme sırasında kaynakları almak için fetch() işlevini çağırabilir. Örneğin, bir uzantı config_resources/ klasöründe config.json adlı bir JSON yapılandırma dosyası içeriyorsa uzantı dosya içeriğini şu şekilde alabilir:

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

Uzantı kendi dışında bir güvenlik kaynağını (https://www.google.com) kullanmaya çalışırsa ve gerekli çapraz kaynak izinleri istemediği sürece tarayıcı buna izin vermez.

Kaynaklar arası izinler isteme

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 makineleri, eşleşme kalıpları veya her ikisini birden 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ğıdakiler gibi eşleşme kalıpları olabilir:

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

"https://*/" eşleşme kalıbı, erişilebilir tüm alanlara HTTPS erişimi sağlar. Burada eşleşme kalıplarının içerik komut dosyası eşleşme kalıplarına benzer olduğunu ancak ana makineyi izleyen tüm yol bilgilerinin yok sayıldığını unutmayın.

Ayrıca, erişim hem ana makineye hem de şemaya göre 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ı olarak tanımlamalıdır:

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

Fetch() ile XMLHttpRequest() karşılaştırması

fetch(), özellikle hizmet çalışanları için oluşturulmuştur ve eşzamanlı işlemlerden ziyade daha geniş bir web trendi uygular. XMLHttpRequest() API, hizmet çalışanı dışındaki uzantılarda desteklenir. API çağrılması, uzantı hizmet çalışanının getirme işleyicisini tetikler. Yeni çalışmada mümkün olduğunda fetch() tercih edilmelidir.

Güvenlikle ilgili olarak göz önünde bulundurulması gerekenler

Siteler arası komut dosyası güvenlik açıklarından kaçınma

fetch() aracılığıyla alınan kaynakları kullanırken ekran dışındaki dokümanınız, yan paneliniz veya pop-up'ınız, siteler arası komut dosyası çalıştırma kurbanı olmamaya dikkat etmelidir. Ö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 dosyalarını ç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 çapraz kaynak istekleriyle sınırlama

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

Bir uzantının, içerik komut dosyasının bir öğenin fiyatını keşfetmesini sağlamak için kaynaklar arası istek gerçekleştirdiği bir örneği düşünün. Bu pek güvenli olmayan bir yaklaşım da, 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ı oluşturup uzantıyı kaynaklar arası kaynaklara erişim sağlamak için kandırabilir.

Bunun yerine, alınabilecek kaynakları sınırlayan mesaj işleyicileri tasarlayın. Aşağıda, içerik komut dosyası tarafından yalnızca itemId sağlanır, tam URL sağlanmaz.

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 et

Ayrıca, HTTP aracılığıyla alınan kaynaklar konusunda özellikle dikkatli olun. Uzantınız saldırgan bir ağda kullanılıyorsa bir ağ saldırganı ("man-in-the-middle" olarak da bilinir) yanıtı değiştirebilir ve potansiyel olarak uzantınıza saldırabilir. Bunun yerine, mümkün olduğunda HTTPS'yi tercih edin.

İçerik güvenliği politikasını düzenleme

Manifest dosyanıza 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 makinelere izin verilmesi gerekir. Varsayılan politika, ana makinelerle bağlantıları kısıtlamasa da connect-src veya default-src yönergelerini açıkça eklerken dikkatli olun.